Docker Compose (Local Dev)
Run the full Makeugcads stack locally with Docker Compose
This guide walks you through running the complete Makeugcads stack on your local machine using Docker Compose. No cloud accounts or paid licenses are required.
Local validation and production deployment use different paths. Local source builds usually run docker compose -f compose.yml -f compose.local.yml --profile legacy-postgres up --build and explicitly enable embedded PostgreSQL. Production compose.yml pulls prebuilt GHCR images and receives DATABASE_URL / LANGGRAPH_DATABASE_URL from Dokploy Databases. Do not commit override files that contain real secrets or machine-specific settings.
Local vs Production
| Scenario | Image source | Recommended command | Notes |
|---|---|---|---|
| Local development / validation | Build from local source | docker compose -f compose.yml -f compose.local.yml --profile legacy-postgres up --build | Local override replaces web / agents images with build: and explicitly enables embedded PostgreSQL |
| Production Dokploy | Prebuilt GHCR images | Dokploy Redeploy | Pulls web, agents, migrations, and rbac-init, then connects to Dokploy Databases PostgreSQL |
Production compose.yml uses these image variables:
GHCR_IMAGE_PREFIX=ghcr.io/chenhaonan-eth/ugcad
IMAGE_TAG=latestThe resulting images are:
${GHCR_IMAGE_PREFIX}-web:${IMAGE_TAG}
${GHCR_IMAGE_PREFIX}-agents:${IMAGE_TAG}
${GHCR_IMAGE_PREFIX}-migrations:${IMAGE_TAG}
${GHCR_IMAGE_PREFIX}-rbac-init:${IMAGE_TAG}Prerequisites
- Docker Desktop (Windows / macOS) or Docker Engine + Compose (Linux)
- Git
- An LLM API key (OpenAI or compatible)
Step 1 — Clone the Repository
git clone https://github.com/chenhaonan-eth/ugcad.git
cd ugcadStep 2 — Create the .env File
Create a .env file and fill in your secrets. Do not commit .env, and do not put real secrets in documentation, Compose files, or override files:
# ── Image selection (used for production GHCR pulls; optional for local build override)
GHCR_IMAGE_PREFIX=ghcr.io/chenhaonan-eth/ugcad
IMAGE_TAG=latest
# ── Infrastructure ────────────────────────────────────
POSTGRES_PASSWORD=your_strong_password_here
DATABASE_URL=postgresql://ugcad:your_strong_password_here@postgres:5432/ugcad
LANGGRAPH_DATABASE_URL=postgresql://ugcad:your_strong_password_here@postgres:5432/langgraph
REDIS_PASSWORD=your_strong_redis_password
# ── Web ───────────────────────────────────────────────
AUTH_SECRET=your_32_char_secret_here_generate_with_openssl
NEXT_PUBLIC_APP_URL=http://localhost:3000
# ── Inter-service auth ────────────────────────────────
AGENTS_SHARED_SECRET=random_shared_secret
# ── LLM ───────────────────────────────────────────────
OPENAI_API_KEY=sk-your-key-here
# If using a third-party OpenAI-compatible API:
# OPENAI_BASE_URL=https://your-provider.com/v1
# OPENAI_MODEL=gpt-4oGenerate strong secrets with:
openssl rand -base64 32 # for AUTH_SECRET
openssl rand -hex 20 # for passwordsStep 3 — Add a Local Source Build Override
Production compose.yml pulls GHCR images by default. To build from the current source tree locally, create a local-only compose.local.yml:
services:
agents:
build:
context: .
dockerfile: docker/Dockerfile.agents-dev
image: ugcad-agents:local
ports:
- "2024:8000"
web:
build:
context: .
dockerfile: apps/web/Dockerfile
args:
NEXT_PUBLIC_APP_URL: ${NEXT_PUBLIC_APP_URL:-http://localhost:3000}
NEXT_PUBLIC_APP_NAME: ${NEXT_PUBLIC_APP_NAME:-Makeugcads}
NEXT_PUBLIC_THEME: ${NEXT_PUBLIC_THEME:-huxibo}
NEXT_PUBLIC_APPEARANCE: ${NEXT_PUBLIC_APPEARANCE:-system}
NEXT_PUBLIC_DEFAULT_LOCALE: ${NEXT_PUBLIC_DEFAULT_LOCALE:-en}
image: ugcad-web:local
ports:
- "3000:3000"The localhost fallback belongs in this local override only. Production compose.yml requires NEXT_PUBLIC_APP_URL explicitly.
Step 4 — Start All Services
For a local source build with embedded PostgreSQL enabled:
docker compose -f compose.yml -f compose.local.yml --profile legacy-postgres up --buildTo only verify that production GHCR images can be pulled, run:
docker compose upOn first source build this builds the web image (Next.js) and the agents image (LangGraph dev server). Expect 5–15 minutes depending on your connection speed.
The local override uses docker/Dockerfile.agents-dev for the agents service, which runs langgraph dev mode without a paid LangSmith license.
Step 5 — Apply Database Migrations
The web container uses a Next.js standalone build that doesn't include drizzle-kit. Local embedded PostgreSQL should still reuse the repository migration script so it writes public.__ugcad_migrations, matching the production migrations image state mechanism:
POSTGRES_PASSWORD=your_strong_password_here bash scripts/migrate-postgres.sh --composeRun this once after starting from an empty database. The migration script applies SQL files in apps/web/src/config/db/migrations/meta/_journal.json order; reruns print skipped for recorded migrations and applied for new migrations. Do not use a manual for f in ... psql loop that bypasses public.__ugcad_migrations.
Step 6 — Verify and Access
Check service status:
docker compose psWhen using the compose.local.yml override, confirm ports are bound correctly. You should see 0.0.0.0:3000->3000/tcp and 0.0.0.0:2024->8000/tcp:
NAME SERVICE STATUS PORTS
<project>-web-1 web running 0.0.0.0:3000->3000/tcp
<project>-agents-1 agents running 0.0.0.0:2024->8000/tcp
<project>-postgres-1 postgres running 5432/tcp
<project>-redis-1 redis running 6379/tcpWhen using the compose.local.yml override, open http://localhost:3000 in your browser.
When using the compose.local.yml override, test the agents API through the local published port:
curl http://localhost:2024/ok
# → {"ok":true}Troubleshooting
Port shows 3000/tcp without 0.0.0.0: prefix
This port check applies only when using compose.local.yml. Docker Desktop on Windows occasionally fails to bind ports on first container creation. Fix:
docker compose restart webAgents container fails with "License verification failed"
You are using the licensed langchain/langgraphjs-api image. Switch to the dev Dockerfile in the local override: docker/Dockerfile.agents-dev runs langgraph dev without a license.
Connection refused when web calls agents
The agents container may be binding to IPv6 loopback (::1) instead of 0.0.0.0. Check the dev Dockerfile CMD includes --host 0.0.0.0:
CMD ["npx", "@langchain/langgraph-cli", "dev",
"--port", "8000", "--host", "0.0.0.0", "--config", "langgraph.json"]Schema errors on first login
If this is an empty-database initialization and you see errors like column "xyz" does not exist, recreate the test database or test volume and then run the migration step again:
POSTGRES_PASSWORD=your_strong_password_here bash scripts/migrate-postgres.sh --composeIf the database already has tables but public.__ugcad_migrations is empty or out of sync with the journal, read apps/web/src/config/db/migrations/README.md before proceeding. Do not treat a local baseline mismatch as proof that production schema initialization failed.
Updating
Pull new code and rebuild locally with the override:
git pull
docker compose -f compose.yml -f compose.local.yml --profile legacy-postgres up --build -d
POSTGRES_PASSWORD=your_strong_password_here bash scripts/migrate-postgres.sh --composeProduction updates should not build on the server. Let GitHub Actions push new GHCR web, agents, migrations, and rbac-init images, then have Dokploy pull the images selected by GHCR_IMAGE_PREFIX and IMAGE_TAG and run the one-off migrations / rbac-init deployment jobs.