Skip to main content

Overview

This guide covers running Comp with Docker Compose. The stack includes:
ServicePurposePort
migratorRuns Prisma migrations against your database-
seederLoads reference data (frameworks, controls)-
appMain Comp application3000
portalCustomer trust portal3002

Prerequisites

  • Docker Desktop or Docker Engine installed
  • External PostgreSQL 14+ with SSL (e.g., Neon, DigitalOcean, RDS)
  • Resend account for transactional email
  • Trigger.dev account for background workflows

Environment File Layout

Docker uses per-service env files, not a root .env file.
FileServices
packages/db/.envmigrator, seeder
apps/app/.envapp
apps/portal/.envportal
Each service reads only its designated env file via env_file: in docker-compose.yml.

Minimal Required Environment

For a functional Docker deployment, you need:
DATABASE_URL="postgresql://user:pass@host:5432/db?sslmode=require"
# Database
DATABASE_URL="postgresql://user:pass@host:5432/db?sslmode=require"

# Auth
AUTH_SECRET="<openssl rand -base64 32>"
SECRET_KEY="<openssl rand -base64 32>"
BETTER_AUTH_URL="https://app.yourdomain.com"
NEXT_PUBLIC_BETTER_AUTH_URL="https://app.yourdomain.com"
NEXT_PUBLIC_PORTAL_URL="https://portal.yourdomain.com"

# Email
RESEND_API_KEY="re_..."

# Workflows (required)
TRIGGER_SECRET_KEY="tr_..."

# Revalidation
REVALIDATION_SECRET="<random string>"
# Database
DATABASE_URL="postgresql://user:pass@host:5432/db?sslmode=require"

# Auth
BETTER_AUTH_SECRET="<openssl rand -base64 32>"
BETTER_AUTH_URL="https://portal.yourdomain.com"
NEXT_PUBLIC_BETTER_AUTH_URL="https://portal.yourdomain.com"

# Email
RESEND_API_KEY="re_..."
See the Environment Reference for all variables.

Build-Time vs Runtime Variables

PhaseVariablesHow They’re Set
Build-timeNEXT_PUBLIC_*Docker build args in docker-compose.yml
RuntimeEverything elseService env files (.env)
The Dockerfile defines these build args for the app:
  • NEXT_PUBLIC_BETTER_AUTH_URL
  • NEXT_PUBLIC_PORTAL_URL
  • NEXT_PUBLIC_POSTHOG_KEY
  • NEXT_PUBLIC_POSTHOG_HOST
  • NEXT_PUBLIC_IS_DUB_ENABLED
  • NEXT_PUBLIC_GTM_ID
  • NEXT_PUBLIC_LINKEDIN_PARTNER_ID
  • NEXT_PUBLIC_LINKEDIN_CONVERSION_ID
  • NEXT_PUBLIC_GOOGLE_ADS_CONVERSION_LABEL
  • NEXT_PUBLIC_API_URL
For portal, only NEXT_PUBLIC_BETTER_AUTH_URL is used at build time.
docker-compose.yml passes build args from shell environment variables (e.g., ${BETTER_AUTH_URL}). Export these before running docker compose build, or set them inline.

Build & Run

1. Prepare Environment Files

# Copy examples to create your env files
cp packages/db/.env.example packages/db/.env
cp apps/app/.env.example apps/app/.env
cp apps/portal/.env.example apps/portal/.env

# Edit each file with your production values

2. Export Build Args

Export the build-time variables needed by docker-compose.yml:
export BETTER_AUTH_URL="https://app.yourdomain.com"
export BETTER_AUTH_URL_PORTAL="https://portal.yourdomain.com"

3. Build Images

docker compose build --no-cache

4. Run Migrations & Seed

docker compose run --rm migrator
docker compose run --rm seeder
migrator runs prisma migrate deploy — safe to run repeatedly.
seeder upserts reference data (frameworks, controls) — idempotent.

5. Start Services

docker compose up -d app portal

6. Verify Health

curl -s http://localhost:3000/api/health
curl -s http://localhost:3002/

Deploy Trigger.dev Tasks

Trigger.dev runs as a hosted service. Deploy your tasks from your workstation:
cd apps/app
bunx trigger.dev@latest login
bunx trigger.dev@latest deploy
Set TRIGGER_SECRET_KEY in apps/app/.env from your Trigger.dev project settings.

Fresh Install (Optional Clean)

To remove all images and volumes for a clean rebuild:
docker compose down --rmi all --volumes --remove-orphans
docker builder prune --all --force

Logging

Docker Compose is configured with log rotation to prevent disk exhaustion. All services use the json-file driver with these defaults:
SettingValueDescription
max-size10mRotate logs at 10 MB
max-file5Keep up to 5 rotated files (~50 MB per service)
compresstrueCompress rotated files

Viewing Logs

# Tail logs for a specific service
docker compose logs -f app

# Tail multiple services
docker compose logs -f app portal

# View last 100 lines
docker compose logs --tail=100 app

# View logs for one-shot jobs
docker compose logs migrator
docker compose logs seeder

Custom Log Configuration

To adjust logging for a specific service (e.g., if app is particularly chatty), create a docker-compose.override.yml:
services:
  app:
    logging:
      driver: json-file
      options:
        max-size: '50m'
        max-file: '10'
        compress: 'true'
For centralized logging (ELK, Loki, Splunk), you can switch the logging driver to fluentd, gelf, or loki. See Docker’s logging driver documentation.

Production Tips

HTTPS & Domains

Place a reverse proxy (nginx, Caddy, Traefik) in front. Update BETTER_AUTH_URL and NEXT_PUBLIC_BETTER_AUTH_URL to your public HTTPS domains.

Strong Secrets

Generate secrets with openssl rand -base64 32. Rotate periodically.

Database Security

Require SSL. Restrict network access via VPC, IP allowlist, or private networking.

Disk Monitoring

Monitor /var/lib/docker disk usage. Periodically prune unused containers and images.

Troubleshooting

Builds succeed but containers crash at startup

The Dockerfile sets SKIP_ENV_VALIDATION=true at build time, so missing env vars are only caught at runtime. Check logs to identify missing variables:
docker compose logs app
docker compose logs portal
Look for errors mentioning process.env.* or “Missing env var”, then compare with the Environment Reference.

Common Misconfigurations

IssueSolution
Using root .envDocker reads per-service env files only
Missing DATABASE_URLMust be set in all three env files
Missing TRIGGER_SECRET_KEYRequired in apps/app/.env for workflows
Mismatched auth URLsEnsure BETTER_AUTH_URL matches NEXT_PUBLIC_BETTER_AUTH_URL
Build args not exportedExport BETTER_AUTH_URL and BETTER_AUTH_URL_PORTAL before build

Container won’t start

# Check if container exists
docker compose ps -a

# View detailed logs
docker compose logs --tail=100 app

Database connection fails

  1. Verify DATABASE_URL format: postgresql://user:pass@host:5432/db?sslmode=require
  2. Ensure your IP is allowlisted in the database firewall
  3. Test connectivity: docker compose run --rm app sh -c "nc -zv <db-host> 5432"