server-config
Core infrastructure stack for multiple independent web apps on one server.
Includes
- Traefik reverse proxy with automatic TLS certificates (Let's Encrypt)
- Shared PostgreSQL database
- Shared Adminer instance behind Traefik + basic auth
- Shared Docker network so app repositories can join this stack
Setup
- Copy
.env.distto.env. - Set real domains and credentials in
.env. - Generate auth hashes with:
htpasswd -nbB admin 'your-password'
- Escape every
$in hashes as$$before putting them into.env. Quick workaround (auto-escape output):
htpasswd -nbB admin 'your-password' | sed 's/\\$/$$/g'
This prints an .env-ready value such as admin:$$2y$$05$$....
5. Start the stack:
docker compose up -d
On first initialization of the Postgres volume, the POSTGRES_USER from .env becomes the bootstrap superuser (default: admin).
For app repositories
Each app should connect to the same external Docker network:
networks:
web:
external: true
name: web
Use the shared Postgres host postgres on that network with credentials from this stack's .env.
Postgres note
This stack uses the latest Postgres image behavior (v18+), so the volume is mounted at /var/lib/postgresql.
Create DB user per app
Use the helper script to create (or update) a dedicated DB user and create a dedicated database:
./scripts/create-db-user.sh project1_db project1_user
Or provide your own password:
./scripts/create-db-user.sh project1_db project1_user "your-strong-password"
The script creates restricted app users (NOSUPERUSER, NOCREATEDB, NOCREATEROLE, NOREPLICATION) and limits default DB access by revoking PUBLIC access.
Note: Postgres cannot natively block "Adminer UI login" for a user while still allowing that same user/password for app DB connections. If you need this hard enforcement, the next step is a custom Adminer plugin that only allows selected DB usernames (for example only admin).