fix(docker): serialize Coolify builds and harden web/worker image inputs

Avoid Coolify OOM crashes by documenting and scripting sequential service builds, and make Docker builds resilient to local-only .env symlinks and missing .cursor agent paths.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Randall Stillwell 2026-07-08 01:00:21 -05:00
parent e8074dc5fc
commit b513b3b04f
7 changed files with 101 additions and 7 deletions

50
.dockerignore Normal file
View file

@ -0,0 +1,50 @@
# Docker build context is the repo root (see docker/docker-compose.coolify.yml).
# Keep in sync with docker/.dockerignore.
# Dependencies & build outputs
**/node_modules
**/.pnpm-store
**/.next
**/dist
**/.turbo
# Git & IDE (worker image copies .cursor; keep context lean via explicit COPY paths in Dockerfile)
.git
.gitignore
.idea
.vscode
*.swp
*.swo
# Env & secrets (compose should inject at runtime)
.env
.env.*
!.env.example
apps/web/.env
# Logs & caches
logs
**/*.log
npm-debug.log*
pnpm-debug.log*
.turbo
.cache
# Tests & misc
coverage
**/__tests__
**/*.test.ts
**/*.test.tsx
**/*.spec.ts
**/*.spec.tsx
# Docs (not needed in image)
**/*.md
LICENSE
# OS
.DS_Store
Thumbs.db
# Docker (avoid nested contexts)
docker/**/*.md

View file

@ -112,6 +112,14 @@ NVIDIA_API_KEY=""
# ----- MCP Server -----
MCP_SERVER_PORT=3001
# =====================================================================
# Coolify Docker Compose build (8 GB hosts)
# =====================================================================
# Serialize image builds to avoid kernel OOM (exit 255) during export.
# Coolify (8 GB host): set Custom Build Command to `bash scripts/build-coolify.sh`
# so compose builds collab → mcp → orchestrator → worker → web sequentially.
# COMPOSE_PARALLEL_LIMIT is documented for reference but the script is required.
# =====================================================================
# Local Docker Compose only (docker/docker-compose.yml)
# Not needed for Coolify (Coolify uses CT 102's shared Postgres/Redis).

View file

@ -8,9 +8,6 @@
# Git & IDE
.git
.gitignore
.cursor
!.cursor/agents
!.cursor/agents/**
.idea
.vscode
*.swp
@ -20,6 +17,7 @@
.env
.env.*
!.env.example
apps/web/.env
# Logs & caches
**/*.log

View file

@ -126,6 +126,9 @@ ENV NODE_ENV=production
# tsup workers still have breathing room. Below ~3 GB the compile spirals
# into GC churn and gets oom-killed (exit 255 with no Docker error).
ENV NODE_OPTIONS=--max-old-space-size=5120
# Local dev uses a gitignored apps/web/.env symlink; drop it so Next.js does not
# follow a broken link inside the container.
RUN rm -f apps/web/.env apps/web/.env.local apps/web/.env.production apps/web/.env.development
RUN pnpm exec turbo build --filter=@tasks/web
@ -272,7 +275,9 @@ COPY --from=worker-build --chown=worker:nodejs /app/apps/worker/node_modules ./a
COPY --chown=worker:nodejs apps ./repo/apps
COPY --chown=worker:nodejs packages ./repo/packages
COPY --chown=worker:nodejs plans ./repo/plans
COPY --chown=worker:nodejs .cursor ./repo/.cursor
COPY --chown=worker:nodejs .cursor/mcp.json ./repo/.cursor/mcp.json
COPY --chown=worker:nodejs .cursor/rules ./repo/.cursor/rules
RUN mkdir -p /app/repo/.cursor/agents
COPY --chown=worker:nodejs pnpm-workspace.yaml package.json turbo.json tsconfig.json ./repo/
# Minimal git repo so worktrees + allowlisted git commands work on the deploy snapshot.

View file

@ -31,6 +31,12 @@
# compile. The `depends_on` below only orders runtime startup, NOT image
# builds — that was a misconception from an earlier iteration of this
# file.
#
# Runtime image export still builds five targets in parallel by default,
# which OOMs 8 GB Coolify hosts (exit 255, no Docker error). Required:
# Coolify → Advanced → Custom Build Command: bash scripts/build-coolify.sh
# (COMPOSE_PARALLEL_LIMIT alone is not reliable across Compose versions.)
# Local verify: pnpm docker:build:coolify
services:
web:
@ -156,7 +162,7 @@ services:
healthcheck:
test:
- CMD-SHELL
- "pgrep -f 'node.*dist/index\\.mjs' > /dev/null || exit 1"
- "pgrep -f 'node.*dist/index\\.js' > /dev/null || exit 1"
interval: 30s
timeout: 5s
retries: 3
@ -187,7 +193,7 @@ services:
healthcheck:
test:
- CMD-SHELL
- "pgrep -f 'node.*dist/index\\.mjs' > /dev/null || exit 1"
- "pgrep -f 'node.*dist/index\\.js' > /dev/null || exit 1"
interval: 30s
timeout: 5s
retries: 3

View file

@ -10,7 +10,8 @@
"db:generate": "turbo db:generate --filter=@tasks/database",
"db:migrate": "turbo db:migrate --filter=@tasks/database",
"db:push": "turbo db:push --filter=@tasks/database",
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,css,md}\""
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,css,md}\"",
"docker:build:coolify": "bash scripts/build-coolify.sh"
},
"devDependencies": {
"@vitest/coverage-v8": "^2",

26
scripts/build-coolify.sh Executable file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env bash
# Coolify custom build command for docker-compose.coolify.yml.
#
# Set in Coolify → Resource → Advanced → Custom Build Command:
# bash scripts/build-coolify.sh
#
# Coolify auto-injects -f and --env-file; this script only serializes builds so
# an 8 GB host does not OOM (exit 255) while exporting five runtime images.
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$repo_root"
export DOCKER_BUILDKIT=1
compose() {
docker compose --project-directory "$repo_root" \
-f docker/docker-compose.coolify.yml \
"$@"
}
# Build one service at a time so an 8 GB host does not OOM during export
# (Coolify default runs all five targets in parallel → exit 255).
for service in collab mcp orchestrator worker web; do
compose build --pull "$service"
done