From b513b3b04f06d361403f2d39150b2ab50282a83c Mon Sep 17 00:00:00 2001 From: Randall Stillwell Date: Wed, 8 Jul 2026 01:00:21 -0500 Subject: [PATCH] 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 --- .dockerignore | 50 +++++++++++++++++++++++++++++++ .env.example | 8 +++++ docker/.dockerignore | 4 +-- docker/Dockerfile | 7 ++++- docker/docker-compose.coolify.yml | 10 +++++-- package.json | 3 +- scripts/build-coolify.sh | 26 ++++++++++++++++ 7 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 .dockerignore create mode 100755 scripts/build-coolify.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2862976 --- /dev/null +++ b/.dockerignore @@ -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 diff --git a/.env.example b/.env.example index c0ea090..9b0336a 100644 --- a/.env.example +++ b/.env.example @@ -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). diff --git a/docker/.dockerignore b/docker/.dockerignore index c47474d..4c156b7 100644 --- a/docker/.dockerignore +++ b/docker/.dockerignore @@ -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 diff --git a/docker/Dockerfile b/docker/Dockerfile index 4d69964..ac80235 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -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. diff --git a/docker/docker-compose.coolify.yml b/docker/docker-compose.coolify.yml index 1a2d030..02dec3d 100644 --- a/docker/docker-compose.coolify.yml +++ b/docker/docker-compose.coolify.yml @@ -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 diff --git a/package.json b/package.json index 38e27e3..747b256 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/build-coolify.sh b/scripts/build-coolify.sh new file mode 100755 index 0000000..a8004bb --- /dev/null +++ b/scripts/build-coolify.sh @@ -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