because I got bored of customising my CV for every job
1services:
2 db:
3 image: postgres:16-alpine
4 environment:
5 POSTGRES_USER: ${POSTGRES_USER:-cv}
6 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-cv}
7 POSTGRES_DB: ${POSTGRES_DB:-cv}
8 ports:
9 - "${DB_PORT:-5432}:5432"
10 volumes:
11 - db-data:/var/lib/postgresql/data
12 healthcheck:
13 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-cv} -d ${POSTGRES_DB:-cv}"]
14 interval: 10s
15 timeout: 5s
16 retries: 3
17
18 server:
19 build:
20 context: .
21 dockerfile: .docker/server.Dockerfile
22 target: development
23 additional_contexts:
24 project-q: ${PROJECT_Q_PATH:?Set PROJECT_Q_PATH to your local project-q checkout}
25 nest-service-locator: ${NEST_SERVICE_LOCATOR_PATH:-/Users/niels/Developer/riotbyte/nest-service-locator}
26 environment:
27 PORT: ${SERVER_PORT:-3000}
28 JWT_SECRET: ${JWT_SECRET:-your-super-secret-jwt-key-here}
29 JWT_ACCESS_TOKEN_EXPIRY: ${JWT_ACCESS_TOKEN_EXPIRY:-15m}
30 JWT_REFRESH_TOKEN_EXPIRY: ${JWT_REFRESH_TOKEN_EXPIRY:-7d}
31 DATABASE_URL: ${DATABASE_URL:-postgresql://cv:cv@db:5432/cv}
32 POSTGRES_USER: ${POSTGRES_USER:-cv}
33 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-cv}
34 POSTGRES_DB: ${POSTGRES_DB:-cv}
35 ENCRYPTION_KEY: ${ENCRYPTION_KEY:-dev-encryption-key-32-chars-long!}
36 RESEND_API_KEY: ${RESEND_API_KEY:-}
37 LLAMA_URL: ${LLAMA_URL:-http://host.docker.internal:8080}
38 AI_TIMEOUT: ${AI_TIMEOUT:-300000}
39 AI_MAX_TOKENS: ${AI_MAX_TOKENS:-8192}
40 PDF_OUTPUT_DIR: /app/pdf-output
41 depends_on:
42 db:
43 condition: service_healthy
44 ports:
45 - "${SERVER_PORT:-3000}:3000"
46 volumes:
47 - ./apps/server/src:/app/apps/server/src
48 - ./apps/server/prisma:/app/apps/server/prisma
49 - ./packages/ai-parser/src:/app/packages/ai-parser/src
50 - ./packages/ai-provider/src:/app/packages/ai-provider/src
51 - ./packages/auth/src:/app/packages/auth/src
52 - ./packages/cv-renderer/src:/app/packages/cv-renderer/src
53 - ./packages/file-upload/src:/app/packages/file-upload/src
54 - ./packages/system/src:/app/packages/system/src
55 - worker-output:/app/pdf-output:ro
56 command: sh -c "cd /app/apps/server && pnpm prisma generate && pnpm prisma:deploy && pnpm dev"
57 healthcheck:
58 test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
59 interval: 15s
60 timeout: 5s
61 retries: 3
62 start_period: 30s
63
64 client:
65 build:
66 context: .
67 dockerfile: .docker/client.Dockerfile
68 target: development
69 environment:
70 VITE_SERVER_URL: ${VITE_SERVER_URL:-http://localhost:3000}
71 VITE_PROXY_TARGET: http://server:3000
72 VITE_DOCS_URL: ${VITE_DOCS_URL:-http://localhost:3001}
73 GRAPHQL_SCHEMA_URL: ${GRAPHQL_SCHEMA_URL:-http://server:3000/graphql}
74 depends_on:
75 server:
76 condition: service_healthy
77 ports:
78 - "${CLIENT_PORT:-5173}:5173"
79 volumes:
80 - ./apps/client/src:/app/apps/client/src
81 - ./packages/routing/src:/app/packages/routing/src
82 - ./packages/ui/src:/app/packages/ui/src
83 - ./packages/system/src:/app/packages/system/src
84 command: sh -c "cd /app/apps/client && (pnpm codegen || true) && pnpm dev"
85 healthcheck:
86 test: ["CMD", "curl", "-f", "http://localhost:5173"]
87 interval: 15s
88 timeout: 5s
89 retries: 3
90 start_period: 20s
91
92 worker:
93 build:
94 context: .
95 dockerfile: .docker/worker.Dockerfile
96 target: development
97 additional_contexts:
98 project-q: ${PROJECT_Q_PATH:?Set PROJECT_Q_PATH to your local project-q checkout}
99 nest-service-locator: ${NEST_SERVICE_LOCATOR_PATH:-/Users/niels/Developer/riotbyte/nest-service-locator}
100 environment:
101 DATABASE_URL: ${DATABASE_URL:-postgresql://cv:cv@db:5432/cv}
102 QUEUE_SCHEMA: ${QUEUE_SCHEMA:-queue}
103 QUEUE_NAME: ${QUEUE_NAME:-default}
104 POLL_INTERVAL_MS: ${POLL_INTERVAL_MS:-1000}
105 PDF_OUTPUT_DIR: /app/pdf-output
106 HEARTBEAT_FILE_PATH: /tmp/worker-heartbeat
107 depends_on:
108 db:
109 condition: service_healthy
110 volumes:
111 - ./apps/worker/src:/app/apps/worker/src
112 - worker-output:/app/pdf-output
113 restart: unless-stopped
114 healthcheck:
115 test: ["CMD-SHELL", "find /tmp/worker-heartbeat -mmin -1 | grep -q ."]
116 interval: 30s
117 timeout: 5s
118 retries: 3
119 start_period: 10s
120
121 docs:
122 build:
123 context: .
124 dockerfile: .docker/docs.Dockerfile
125 target: development
126 environment:
127 VITE_CLIENT_URL: ${VITE_CLIENT_URL:-http://localhost:5173}
128 VITE_SERVER_URL: ${VITE_SERVER_URL:-http://localhost:3000}
129 ports:
130 - "${DOCS_PORT:-3001}:3001"
131 volumes:
132 - ./apps/docs/src:/app/apps/docs/src
133 - ./apps/docs/content:/app/apps/docs/content
134 - ./packages/routing/src:/app/packages/routing/src
135 - ./packages/ui/src:/app/packages/ui/src
136 - ./packages/system/src:/app/packages/system/src
137 healthcheck:
138 test: ["CMD", "curl", "-f", "http://localhost:3001"]
139 interval: 15s
140 timeout: 5s
141 retries: 3
142 start_period: 20s
143
144 model-download:
145 image: alpine:latest
146 volumes:
147 - ./ai-models:/models
148 command: |
149 sh -c '
150 MODEL_FILE="/models/mistral-7b-instruct-v0.2.Q4_K_M.gguf"
151 if [ -f "$$MODEL_FILE" ]; then
152 echo "Model already exists, skipping download"
153 exit 0
154 fi
155 echo "Downloading Mistral 7B model (~4.4GB)..."
156 apk add --no-cache wget
157 wget -c -O "$$MODEL_FILE" \
158 "https://huggingface.co/TheBloke/Mistral-7B-Instruct-v0.2-GGUF/resolve/main/mistral-7b-instruct-v0.2.Q4_K_M.gguf"
159 echo "Model download complete"
160 '
161
162 llama:
163 # CPU-only llama.cpp server (slow: ~6 tokens/sec)
164 # For GPU acceleration:
165 # - macOS: Use hybrid mode with native Metal (see DOCKER_GPU.md)
166 # - Linux: Use docker-compose.nvidia.yml for NVIDIA GPU support
167 image: ghcr.io/ggml-org/llama.cpp:server
168 profiles:
169 - docker-llama
170 ports:
171 - "${LLAMA_PORT:-8080}:8080"
172 volumes:
173 - ./ai-models:/models
174 command: -m /models/mistral-7b-instruct-v0.2.Q4_K_M.gguf --port 8080 --host 0.0.0.0 -c 16384 -ngl 0 # -ngl 0 = CPU only
175 depends_on:
176 model-download:
177 condition: service_completed_successfully
178 healthcheck:
179 test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
180 interval: 30s
181 timeout: 10s
182 retries: 3
183 start_period: 30s
184
185volumes:
186 db-data:
187 worker-output: