Quistgame is a Nuxt/PostgreSQL quiz app. The default deployment runs PostgreSQL, the Nuxt server and an nginx HTTPS reverse proxy through Docker Compose.
- Docker and Docker Compose
- Node.js and npm for host-side development and tests
- OpenSSL for local certificate generation
- Optional: an ACME client such as lego for a trusted certificate
Copy the environment templates:
cp .env.example .env
cp .env.migrations.example .env.migrationsGenerate and fill in the required secrets:
openssl rand -base64 32 # QUIZ_APP_PASSWORD
openssl rand -base64 32 # POSTGRES_PASSWORD
openssl rand -base64 32 # QUIZ_MIGRATIONS_PASSWORD
openssl rand -hex 32 # ENCRYPTION_KEYUse .env for application runtime values:
QUIZ_APP_PASSWORD: database password for the app roleENCRYPTION_KEY: 32-byte hex key for encrypted account fieldsAPP_DOMAIN: public or intranet domain, defaultintranet-kevin.nebelnidas.netAPP_BASE_URL: HTTPS base URL used for generated linksCOOKIE_SECURE=true: required for the HTTPS deployment pathDB_SSL=true,DB_SSL_MODE=verify-ca,DB_SSL_CA_PATH=db/certs/ca/ca.crtHIBP_MODE=online-fallback: checks passwords against Have I Been Pwned when the API is reachableSMTP_*: optional SMTP settings for password reset emails
Use .env.migrations only for database bootstrap and migration credentials:
POSTGRES_PASSWORD: PostgreSQL superuser passwordQUIZ_MIGRATIONS_PASSWORD: password for the migration role
No URL encoding is needed. The app and migration runner build the PostgreSQL connection from individual environment variables.
Generate the local database CA and server certificate once:
# Bash
bash db/generate-certs.sh
# PowerShell
.\db\generate-certs.ps1The database accepts application and migration connections only through TLS.
Host-side migration commands therefore need these values in .env:
DB_SSL=true
DB_SSL_MODE=verify-ca
DB_SSL_CA_PATH=db/certs/ca/ca.crt
DB_SSL_SERVERNAME=postgres
For the project deployment, place the HTTPS certificate files here:
nginx/certs/fullchain.pem
nginx/certs/privkey.pem
The intranet deployment uses intranet-kevin.nebelnidas.net. Because the service
does not need to be reachable from the public internet, the certificate can be
issued with an ACME DNS challenge, for example through lego.
For a local HTTPS smoke test without a public CA, generate a self-signed certificate:
bash nginx/generate-dev-cert.shThe browser will warn about this local certificate unless it is trusted manually.
Start the full stack:
docker compose up -d --buildCompose starts:
db: PostgreSQL with TLS enabledquiz-app: Nuxt app on the internal Docker networknginx: HTTPS reverse proxy on ports 80 and 443
Migrations run automatically when the app container starts. The web app is available through nginx:
https://intranet-kevin.nebelnidas.net
For local tests without DNS, either open:
https://localhost
or add this hosts entry and open the intranet domain:
127.0.0.1 intranet-kevin.nebelnidas.net
Check the containers:
docker compose psCheck the HTTPS endpoint and response headers:
curl -k -I https://localhostThe response should include headers such as:
Strict-Transport-SecurityContent-Security-PolicyX-Content-Type-OptionsReferrer-PolicyPermissions-Policy
Run the automated checks:
npm run lint
npm run editorconfig:check
npm test
npm run test:integration
npm run buildnpm run test:integration uses https://127.0.0.1 and disables certificate
verification only for the local self-signed test certificate.
To run only PostgreSQL in Docker and the Nuxt dev server on the host:
docker compose up -d db
npm run db:up
npm run devThis development path does not use nginx. For plain HTTP development at
http://localhost:3000, temporarily use these local values in .env:
APP_BASE_URL=http://localhost:3000
COOKIE_SECURE=false
For security-flow testing, prefer the full Docker setup because it exercises HTTPS, secure cookies and the reverse proxy.
npm run db:status # check applied / pending
npm run db:down # roll back last migration
npm run db:new # scaffold a new migration- If
https://intranet-kevin.nebelnidas.netdoes not resolve locally, add the hosts entry shown above or usehttps://localhost. - If nginx does not start, check that
nginx/certs/fullchain.pemandnginx/certs/privkey.pemexist. - If PostgreSQL rejects host connections, verify the DB certificate files and
keep
DB_SSL_MODE=verify-cafor the current local certificates. - If local build files are owned by root after Docker runs, stop the app container and fix ownership before running host-side build commands.
docker compose stop quiz-app nginx
sudo chown -R "$USER:$USER" .nuxt .output