Skip to main content
Docker Compose lets you run the full Bloom stack locally, including the API, public site, partners site, PostgreSQL database, and an nginx load balancer. This is the closest local approximation of the production cloud environment. Docker and Docker Compose are required. The easiest way to install them is via Docker Desktop. Podman is also supported if you manually install Docker Compose.
If running on macOS Apple Silicon, Docker and Podman run a Linux VM. Ensure the VM has sufficient resources:
  • 8 CPU
  • 12 GB memory
  • 200 GB disk space

Quick start

1

Build the images

docker compose build
2

Start the stack

docker compose up
3

Open the sites

Services

The following services are defined in docker-compose.yml:

lb

An nginx load balancer that fronts the api, partners, and public containers. Required to support running multiple replicas of these services.

db

PostgreSQL 18 database. Exposed on port 5432. Health-checked before dependent services start.

dbinit

Runs the database initialization script from api/dbinit. Executes once on startup after db is healthy.

dbseed

Seeds the database using yarn db:seed:staging. Runs after the API has started and applied migrations.

api

The Bloom API server. Runs DB migrations on start via yarn db:migration:run && yarn start:prod. Exposed internally on port 3100.

partners

The partners Next.js site. Runs next build at container start, then serves on port 3001.

public

The public-facing Next.js site. Runs next build at container start, then serves on port 3000.

pgadmin

Optional pgAdmin 4 UI for inspecting the database. Enabled via the pgadmin profile. Accessible at http://localhost:3200.

Port mappings

ServiceHost portContainer portDescription
lb30003000Public site (proxied via nginx)
lb30013001Partners site (proxied via nginx)
lb31003100API (proxied via nginx)
db54325432PostgreSQL
pgadmin32003200pgAdmin (optional)

Dockerfiles

API (api/Dockerfile)

Uses a multi-stage build to minimize the final image size:
  1. Build stage — Installs all dependencies, generates the Prisma client, and compiles TypeScript to dist/.
  2. Run stage — Copies only the compiled output and runtime dependencies from the build stage. Runs as a non-root user (bloom_api, UID 2002).
On startup the container runs:
yarn db:migration:run && yarn start:prod
Resource limits (kept in sync with ECS task definitions):
  • vCPU: 1
  • Memory: 2 GiB

Public site (Dockerfile.sites.public)

Builds from node:22. Installs dependencies using a cached layer (copies package.json and yarn.lock before source code), then copies the full source. Runs as a non-root user (next, UID 2002). On startup the container runs a Next.js build followed by the production server:
NODE_OPTIONS='--max-semi-space-size=128 --max-old-space-size=4096' yarn build && yarn start
Resource limits:
  • vCPU: 2
  • Memory: 6 GiB
next build runs at container start time and requires a live API and database to succeed. Resource usage spikes to the limit during the build, then drops significantly once the server starts serving traffic.

Partners site (Dockerfile.sites.partners)

Structurally identical to the public site Dockerfile. Uses lower Node.js memory limits:
NODE_OPTIONS='--max-semi-space-size=64 --max-old-space-size=2048' yarn build && yarn start
Resource limits:
  • vCPU: 2
  • Memory: 4 GiB

Environment variables

Key environment variables configured in docker-compose.yml:
VariableValueDescription
PORT3100API listen port
NODE_ENVproductionRuntime environment
APP_SECRETtotally a secretApplication secret (dev only)
DATABASE_URLpostgres://bloom_api:bloom_api_pw@db:5432/bloom_prismaDatabase connection string
DB_NO_SSLTRUEDisables SSL for local DB

Common commands

Build, start, and stop

# Build all images
docker compose build

# Build specific services
docker compose build api partners

# Start all services
docker compose up

# Stop and remove containers (wipes database state)
docker compose down

Multiple replicas

By default each of api, partners, and public runs a single replica. Control the replica count with environment variables:
API_REPLICAS=3 PARTNERS_REPLICAS=3 PUBLIC_REPLICAS=3 docker compose up

pgAdmin

Start pgAdmin alongside the stack to inspect the database:
# Option 1: start everything including pgadmin
COMPOSE_PROFILES=pgadmin docker compose up

# Option 2: start pgadmin when the stack is already running
docker compose start pgadmin
Access pgAdmin at http://localhost:3200:
  • Username: [email protected]
  • Password: abcdef
  • DB user: bloom_readonly
  • DB password: bloom_readonly_pw

Restarting and rebuilding

When you exit docker compose up, containers are stopped but not deleted. If you run docker compose up again, the database retains its prior state and the dbseed container will fail because it already seeded the database. To avoid this, either:
  • Run docker compose down between runs to wipe the database state.
  • Restart only the application containers to skip re-seeding:
    docker compose up lb db api partners public
    
To force a fresh image build:
docker compose build

Debugging

Run a command in a running container

Find the container ID:
docker container ls
Run a command inside a container (example: list Next.js server chunks in the public site):
docker exec 62de5c066288 ls /app/sites/public/.next/server/chunks
Open an interactive shell:
docker exec -it 62de5c066288 /bin/bash

Start an additional container in the network

The Compose stack creates a network called bloom_default. Attach any container to it with --network=bloom_default:
docker container run --network=bloom_default --rm -it python:latest /bin/bash
# Inside the container:
curl api:3100/jurisdictions | python -m json

Database seeding

By default the dbseed service runs yarn db:seed:staging. To use the development seed script instead, edit the dbseed service in docker-compose.yml:
dbseed:
  build:
    context: ./api
    dockerfile: Dockerfile.dbseed.dev
  restart: no
  environment:
    DATABASE_URL: "postgres://postgres:example@db:5432/bloom_prisma"
  command:
    - "yarn"
    - "db:seed:development"
  depends_on:
    api:
      condition: service_healthy

Build docs developers (and LLMs) love