Skip to main content
OpenCouncil provides a flexible Docker setup that supports both development and production deployments with local or remote databases.

Prerequisites

  • Docker Engine 20.10+
  • Docker Compose 2.0+
  • At least 4GB RAM available for containers
  • .env file configured (see Environment setup)

Quick start

1

Make the run script executable

chmod +x run.sh
2

Copy environment file

cp .env.example .env
# Edit .env with your configuration
3

Start in development mode

# With local database (default)
./run.sh

# With remote database
./run.sh --remote-db
The application will be available at http://localhost:3000

Development mode

Starts both the application and a PostgreSQL + PostGIS container:
./run.sh
This configuration:
  • ✅ Automatically runs migrations
  • ✅ Seeds the database with sample data
  • ✅ Hot-reloads code changes
  • ✅ Includes Prisma Studio on port 5555
The local database uses Docker volumes for persistence. Data survives container restarts but will be lost if you run ./run.sh --clean.

Remote database

Connects to an existing database using DATABASE_URL from .env:
./run.sh --remote-db
Remote database mode runs migrations automatically but does not seed to protect production data.

Force rebuild

Rebuild containers after changing dependencies:
./run.sh -- --build

# With no cache
./run.sh -- --build --no-cache

Production mode

./run.sh --prod
Production mode:
  • Builds optimized Next.js production bundle
  • Runs with NODE_ENV=production
  • Disables hot-reload and development tools
  • Uses standalone output for minimal image size

Running multiple instances

The run.sh script automatically detects port conflicts and finds available ports. This enables running multiple instances simultaneously (useful for git worktrees):
# First instance (uses ports 3000, 5555, 5432)
cd ~/projects/opencouncil-main
./run.sh

# Second instance (automatically uses 3001, 5556, 5433)
cd ~/projects/opencouncil-feature-branch
./run.sh

# Third instance (automatically uses 3002, 5557, 5434)
cd ~/projects/opencouncil-another-feature
./run.sh
Manually specify ports if needed:
./run.sh --app-port 3001 --prisma-port 5556 --db-port 5433

Docker Compose profiles

OpenCouncil uses Docker Compose profiles to control which services run:
ProfileServicesUse case
devapp-devDevelopment with hot-reload
prodappProduction build
with-dbdbLocal PostgreSQL + PostGIS

Running just the database

Use the database container independently:
# Foreground
docker compose --profile with-db up db

# Background
docker compose --profile with-db up -d db
Connect to it from the host:
psql "postgresql://postgres:postgres@localhost:5432/development"

Running commands in containers

The exec.sh script provides a convenient way to run commands inside containers:
# Generate Prisma client
./exec.sh npx prisma generate

# Run migrations
./exec.sh npx prisma migrate dev

# Open Prisma Studio
./exec.sh npx prisma studio
The exec.sh script:
  • Loads environment variables from .env
  • Starts the container if not running
  • Executes commands with the proper environment

Database connection modes

The Docker setup handles database connections automatically based on the mode:

Local database mode (./run.sh)

Connection strings are automatically constructed from initialization variables:
# From .env (initialization only)
DATABASE_USER=postgres
DATABASE_PASSWORD=postgres
DATABASE_NAME=development

# Automatically set at runtime
DATABASE_URL="postgresql://postgres:postgres@db:5432/development?sslmode=disable"
DIRECT_URL="postgresql://postgres:postgres@db:5432/development?sslmode=disable"

Remote database mode (./run.sh --remote-db)

Uses connection strings directly from .env:
DATABASE_URL="postgresql://user:pass@host:5432/dbname?sslmode=require"
DIRECT_URL="postgresql://user:pass@host:5432/dbname?sslmode=require"
You can keep your .env configured for remote databases. The local mode automatically overrides these values.

Docker networks

OpenCouncil uses Docker networks for service communication:

Default shared network

All instances share opencouncil-net by default:
./run.sh  # Uses opencouncil-net
This allows multiple OpenCouncil instances to communicate with a single task server.

Custom networks (isolated development)

Isolate instances with custom networks:
# Using flag
./run.sh --network feature-auth

# Using environment variable
NETWORK_NAME=feature-auth ./run.sh

# Or add to .env
echo "NETWORK_NAME=feature-auth" >> .env
./run.sh
Custom network names are prefixed with opencouncil-net- (e.g., opencouncil-net-feature-auth).

Connecting to the task server

Configure opencouncil-tasks to use the same network:
docker-compose.yml
networks:
  opencouncil-net:
    name: opencouncil-net  # or opencouncil-net-feature-auth
    external: true
Then set the connection in .env:
# OpenCouncil → Task server
TASK_API_URL=http://opencouncil-tasks-app-1:3005

# Task server → OpenCouncil (callbacks)
NEXTAUTH_URL=http://opencouncil-app-dev-1:3000

Cleaning up resources

Remove all Docker resources for the current directory:
./run.sh --clean
This command:
  • Stops and removes containers
  • Removes volumes (including database data)
  • Removes networks
  • Removes configuration files
For git worktrees: Always run ./run.sh --clean before removing a worktree to avoid orphaned Docker resources.

Troubleshooting

Port already in use

The script automatically finds available ports. If you manually specify a port that’s in use:
lsof -i :3000  # Find process using port 3000
kill <PID>    # Stop the process

Container won’t start

Check logs:
docker compose logs app
docker compose logs db
Rebuild with no cache:
./run.sh -- --build --no-cache

Database connection failed

Verify database is running:
docker compose ps db
Test connection:
docker compose exec db pg_isready -U postgres

Cached data after switching databases

Clear Next.js cache:
rm -rf .next
./run.sh

Next steps

Environment setup

Configure environment variables

Database setup

Migrations and seeding

Build docs developers (and LLMs) love