Skip to content

Docker Development Setup for ERP-Unlocked Monorepo

This document provides comprehensive instructions for Docker-based development in the ERP-Unlocked monorepo, covering both containerized and hybrid development approaches.

  1. Prerequisites
  2. Development Environment Setup
  3. Docker Compose Development
  4. Hybrid Development (Recommended)
  5. Building with Docker Bake
  6. Individual App Development
  7. Database Operations
  8. Troubleshooting
  • Docker Desktop (4.0+)
  • Node.js (22.22.0+, matching .nvmrc) with pnpm (10+, 10.33.4)
  • Python (3.11+) with uv (for Python packages)
  • Git for version control
Terminal window
corepack enable
corepack prepare pnpm@10.33.4 --activate
Terminal window
docker --version
node --version
pnpm --version
python --version
Terminal window
git clone https://github.com/ERP-Unlocked/ordermatic.git
cd ordermatic
# Use pinned Node version (22.22.0)
nvm use
# Install all dependencies using pnpm workspaces
pnpm install
# Install Python dependencies for root uv workspace
# (members: packages/pdf-shared, apps/temporal-worker, packages/feature-flags-py, packages/erp-sql-py;
# note: apps/dagster has its own separate uv.lock)
uv sync

Create environment files for root shared infrastructure and the webapp:

Terminal window
# Root shared environment (Database, Redis, R2, Gemini, Temporal)
cp .env.example .env
# Webapp environment (Clerk auth, public URLs)
cp apps/webapp/.env.example apps/webapp/.env

Edit the environment files with your local configuration:

Root .env and apps/webapp/.env:

DATABASE_URL=postgresql://postgres:password@localhost:5432/erp_unlocked
REDIS_URL=redis://localhost:6379
CLERK_SECRET_KEY=your_clerk_secret_key
PUBLIC_CLERK_PUBLISHABLE_KEY=your_clerk_publishable_key
GEMINI_API_KEY=your_gemini_api_key
TEMPORAL_ADDRESS=localhost:7233
Terminal window
# Start all services in background
docker-compose up -d
# Start with logs visible
docker-compose up
Terminal window
# Start only infrastructure services (PostgreSQL, Redis)
docker-compose up -d postgres redis
# Start webapp and its dependencies
docker-compose up webapp postgres redis
# Start PDF extraction (Temporal server + worker)
docker-compose up temporal temporal-worker redis postgres
Terminal window
# View service status
docker-compose ps
# View logs from all services
docker-compose logs -f
# View logs from specific service
docker-compose logs -f webapp
# Stop all services
docker-compose down
# Stop and remove volumes (clean slate)
docker-compose down -v

For optimal development experience, run infrastructure in Docker but apps natively:

Terminal window
# Start PostgreSQL, Redis, and Temporal
docker-compose up -d postgres redis temporal temporal-ui
# Verify services are running
docker-compose ps

Terminal 1 - Webapp (Astro + React):

Terminal window
pnpm --filter webapp dev
# → http://localhost:4321

Terminal 2 - Temporal worker (PDF extraction and order automation):

Terminal window
set -a; . ./.env; set +a
uv run python apps/temporal-worker/worker.py
Terminal window
pnpm --filter @repo/db db:migrate
pnpm --filter @repo/db db:seed:billing # Optional: seed billing plans

Docker Bake provides unified build configuration for targets in docker-bake.hcl:

Terminal window
# Build default targets (webapp, ts-temporal-worker)
docker buildx bake
# Build and push to registry (for CI/CD)
docker buildx bake --push
# Build with custom tag
BRANCH_NAME=feature/my-feature COMMIT_SHA=abc123 docker buildx bake
Terminal window
# Build only webapp
docker buildx bake webapp
# Build TypeScript Temporal worker
docker buildx bake ts-temporal-worker

The docker-bake.hcl file defines build targets:

  • webapp / webapp-production: Astro customer application
  • ts-temporal-worker / ts-temporal-worker-production: TypeScript Temporal worker
  • Groups: default (webapp, ts-temporal-worker), production (webapp-production, ts-temporal-worker-production), webapp
Terminal window
cd apps/webapp
# Development server
pnpm run dev # Start dev server on port 4321
pnpm run build # Build for production
pnpm run preview # Preview production build
# Database operations (from repo root)
pnpm --filter @repo/db db:migrate # Run database migrations
pnpm --filter @repo/db db:studio # Open Drizzle Studio
# Testing and linting
pnpm run test # Run tests
pnpm run lint # Lint code
pnpm turbo:typecheck # TypeScript type checking across workspace
Terminal window
# Schema operations
pnpm --filter @repo/db db:generate # Generate migration from schema changes
pnpm --filter @repo/db db:migrate # Apply pending migrations
pnpm --filter @repo/db db:push # Push schema directly (dev only)
# Development utilities
pnpm --filter @repo/db db:studio # Open Drizzle Studio (GUI)
pnpm --filter @repo/db db:seed:billing # Seed billing plans
Terminal window
# Connect to PostgreSQL container
docker exec -it erp_unlocked_postgres psql -U postgres -d erp_unlocked
# Common SQL operations
\dt # List tables
\d users # Describe table structure
SELECT * FROM users LIMIT 5; # Query data
Terminal window
cd packages/ui
# Development
pnpm run dev # Storybook development server
pnpm run build # Build component library
pnpm run test # Test components
# Generate new component
pnpm run generate:component MyComponent
Terminal window
cd packages/utils
# Development and testing
pnpm run build # Build TypeScript utilities
pnpm run test # Run unit tests
pnpm run typecheck # TypeScript validation
  1. Start infrastructure: docker-compose up -d postgres redis temporal temporal-ui
  2. Run database migrations: pnpm --filter @repo/db db:migrate
  3. Start webapp: pnpm --filter webapp dev
  4. Start worker: set -a; . ./.env; set +a; uv run python apps/temporal-worker/worker.py
  1. Start infrastructure: docker-compose up -d postgres redis
  2. Run database migrations: pnpm --filter @repo/db db:migrate
  3. Start webapp: pnpm --filter webapp dev
  1. Start infrastructure: docker-compose up -d postgres redis temporal
  2. Start worker: set -a; . ./.env; set +a; uv run python apps/temporal-worker/worker.py
Terminal window
# Run all JS/TS tests from root
pnpm test
# Run Python tests (packages/pdf-shared + apps/dagster)
pnpm test:python
# Run Temporal worker Python tests
pnpm --filter @repo/temporal-worker test
Terminal window
# Lint all code
pnpm lint
pnpm lint:python
# Format all code
pnpm format
pnpm format:python
# Type checking
pnpm turbo:typecheck
# Pre-commit hooks (automatic)
git commit -m "Your changes" # Runs lint-staged and changeset verification automatically
Terminal window
# Check what's using a port
lsof -i :4321 # Check webapp port
lsof -i :4322 # Check internal portal port
# Kill processes using ports
sudo kill -9 $(lsof -ti:4321)
Terminal window
# Verify PostgreSQL is running
docker-compose ps postgres
# Check database connectivity
docker exec -it erp_unlocked_postgres pg_isready
# Reset database connection
docker-compose restart postgres
Terminal window
# Clear pnpm store cache and reinstall
pnpm store prune
pnpm install
Terminal window
# Clean Docker cache
docker system prune -a
# Rebuild without cache
docker buildx bake --no-cache
# Check Docker Bake configuration
docker buildx bake --print
Terminal window
# Check webapp health
curl http://localhost:4321/api/health
# Check Redis connectivity
redis-cli -h localhost -p 6379 ping
# Check PostgreSQL connectivity
docker exec erp_unlocked_postgres pg_isready
Terminal window
# Application logs
docker-compose logs webapp
docker-compose logs temporal-worker
# Database logs
docker-compose logs postgres
# Redis logs
docker-compose logs redis
Terminal window
# Use pnpm's node_modules linking for faster installs
pnpm config set store-dir ~/.pnpm-store
# Enable pnpm's shamefully-hoist for faster builds
echo "shamefully-hoist=true" >> .npmrc
# Use Docker BuildKit for faster builds
export DOCKER_BUILDKIT=1
Terminal window
# Monitor Docker resource usage
docker stats
# Limit container resources in docker-compose.yml
services:
webapp:
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M

This comprehensive setup ensures efficient development across the entire ERP-Unlocked monorepo, supporting both full containerized development and hybrid approaches based on your preferences and requirements.

The production deployment process differs significantly from local development:

  • PR-Based Workflow: All deployments use strict branch protection with conventional commit formats
  • GitHub Actions: Automated builds triggered by pushes to protected branches (main/staging)
  • Cloud Build: gcp-deploy.yml triggers cloudbuild.yaml, which builds, pushes, and deploys
  • Image Registry: Production images live in GCP Artifact Registry, tagged by commit SHA
  • Two targets: webapp / internal go to Cloud Run; dagster and both Temporal workers are systemd units on GCE VMs
  • Background jobs: Temporal workflows, not a separate job platform

For complete deployment procedures, see the Deployment Guide.