create-carlonicora-app 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +675 -0
- package/README.md +104 -0
- package/bin/cli.js +3 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +92 -0
- package/dist/cli.js.map +1 -0
- package/dist/git.d.ts +7 -0
- package/dist/git.d.ts.map +1 -0
- package/dist/git.js +80 -0
- package/dist/git.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts.d.ts +5 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +30 -0
- package/dist/prompts.js.map +1 -0
- package/dist/replacer.d.ts +9 -0
- package/dist/replacer.d.ts.map +1 -0
- package/dist/replacer.js +11 -0
- package/dist/replacer.js.map +1 -0
- package/dist/scaffold.d.ts +3 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +79 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/types/index.d.ts +16 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/files.d.ts +6 -0
- package/dist/utils/files.d.ts.map +1 -0
- package/dist/utils/files.js +103 -0
- package/dist/utils/files.js.map +1 -0
- package/dist/utils/logger.d.ts +12 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +35 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/validation.d.ts +6 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +63 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +52 -0
- package/template/.env.example +159 -0
- package/template/.github/workflows/check-library-updates.yml +71 -0
- package/template/.github/workflows/dev.yml +63 -0
- package/template/.github/workflows/pull-request.yml +55 -0
- package/template/.gitmodules +6 -0
- package/template/.husky/pre-commit +1 -0
- package/template/.husky/pre-push +1 -0
- package/template/.prettierignore +1 -0
- package/template/.prettierrc +13 -0
- package/template/.releaserc +134 -0
- package/template/.vscode/settings.json +16 -0
- package/template/CHANGELOG.md +0 -0
- package/template/CLAUDE.md +34 -0
- package/template/DOCKER.md +1591 -0
- package/template/Dockerfile +228 -0
- package/template/README.md +1 -0
- package/template/apps/api/.prettierrc +12 -0
- package/template/apps/api/eslint.config.mjs +54 -0
- package/template/apps/api/jest.config.js +29 -0
- package/template/apps/api/nest-cli.json +15 -0
- package/template/apps/api/package.json +155 -0
- package/template/apps/api/src/config/config.ts +17 -0
- package/template/apps/api/src/config/enums/job.name.ts +6 -0
- package/template/apps/api/src/config/enums/queue.id.ts +3 -0
- package/template/apps/api/src/config/interfaces/config.interface.ts +3 -0
- package/template/apps/api/src/features/features.modules.ts +6 -0
- package/template/apps/api/src/i18n/en/notifications.json +3 -0
- package/template/apps/api/src/main.ts +23 -0
- package/template/apps/api/src/neo4j.migrations/20250901_001.ts +33 -0
- package/template/apps/api/src/neo4j.migrations/20250901_002.ts +90 -0
- package/template/apps/api/src/neo4j.migrations/20250901_003.ts +57 -0
- package/template/apps/api/src/neo4j.migrations/20250901_004.ts +32 -0
- package/template/apps/api/src/neo4j.migrations/queries/migration.queries.ts +49 -0
- package/template/apps/api/src/types/langchain.d.ts +56 -0
- package/template/apps/api/tsconfig.build.json +4 -0
- package/template/apps/api/tsconfig.json +38 -0
- package/template/apps/web/.swcrc +26 -0
- package/template/apps/web/components.json +21 -0
- package/template/apps/web/eslint.config.mjs +33 -0
- package/template/apps/web/global.d.ts +7 -0
- package/template/apps/web/messages/en.json +249 -0
- package/template/apps/web/next.config.js +50 -0
- package/template/apps/web/package.json +146 -0
- package/template/apps/web/playwright.config.ts +86 -0
- package/template/apps/web/postcss.config.mjs +5 -0
- package/template/apps/web/public/sw.js +32 -0
- package/template/apps/web/src/app/[locale]/(admin)/administration/companies/[id]/page.tsx +46 -0
- package/template/apps/web/src/app/[locale]/(admin)/administration/companies/page.tsx +23 -0
- package/template/apps/web/src/app/[locale]/(admin)/layout.tsx +49 -0
- package/template/apps/web/src/app/[locale]/(auth)/activation/[code]/page.tsx +13 -0
- package/template/apps/web/src/app/[locale]/(auth)/auth/page.tsx +11 -0
- package/template/apps/web/src/app/[locale]/(auth)/invitation/[code]/page.tsx +7 -0
- package/template/apps/web/src/app/[locale]/(auth)/layout.tsx +9 -0
- package/template/apps/web/src/app/[locale]/(auth)/login/page.tsx +6 -0
- package/template/apps/web/src/app/[locale]/(auth)/logout/page.tsx +5 -0
- package/template/apps/web/src/app/[locale]/(auth)/register/page.tsx +6 -0
- package/template/apps/web/src/app/[locale]/(auth)/reset/[code]/page.tsx +7 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/notifications/page.tsx +9 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/roles/[id]/page.tsx +23 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/roles/page.tsx +12 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/[id]/error.tsx +14 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/[id]/loading.tsx +21 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/[id]/page.tsx +46 -0
- package/template/apps/web/src/app/[locale]/(main)/(foundations)/users/page.tsx +17 -0
- package/template/apps/web/src/app/[locale]/(main)/error.tsx +62 -0
- package/template/apps/web/src/app/[locale]/(main)/layout.tsx +40 -0
- package/template/apps/web/src/app/[locale]/(main)/page.tsx +41 -0
- package/template/apps/web/src/app/[locale]/layout.tsx +54 -0
- package/template/apps/web/src/app/globals.css +256 -0
- package/template/apps/web/src/config/BootstrapProvider.tsx +13 -0
- package/template/apps/web/src/config/Bootstrapper.ts +77 -0
- package/template/apps/web/src/config/env.ts +51 -0
- package/template/apps/web/src/config/middleware-env.ts +14 -0
- package/template/apps/web/src/enums/feature.ids.ts +3 -0
- package/template/apps/web/src/features/common/components/containers/IndexContainer.tsx +11 -0
- package/template/apps/web/src/features/common/components/details/LayoutDetails.tsx +33 -0
- package/template/apps/web/src/features/common/components/navigations/CommonSidebar.tsx +233 -0
- package/template/apps/web/src/features/common/components/navigations/CreationDropDown.tsx +117 -0
- package/template/apps/web/src/features/common/components/navigations/UserSidebarFooter.tsx +115 -0
- package/template/apps/web/src/features/common/components/navigations/VersionDisplay.tsx +18 -0
- package/template/apps/web/src/features/common/contexts/ErrorContext.tsx +62 -0
- package/template/apps/web/src/i18n/request.ts +13 -0
- package/template/apps/web/src/i18n/routing.ts +9 -0
- package/template/apps/web/src/i18n/useDateFnsLocale.ts +15 -0
- package/template/apps/web/src/proxy.ts +107 -0
- package/template/apps/web/src/server-actions/auth-cookies.ts +134 -0
- package/template/apps/web/src/types/modules.d.ts +10 -0
- package/template/apps/web/src/utils/metadata.ts +50 -0
- package/template/apps/web/src/utils/revalidation.ts +7 -0
- package/template/apps/web/tsconfig.json +51 -0
- package/template/docker-compose.yml +211 -0
- package/template/package.json +72 -0
- package/template/packages/nestjs-neo4jsonapi/.gitkeep +0 -0
- package/template/packages/nextjs-jsonapi/.gitkeep +0 -0
- package/template/packages/shared/package.json +23 -0
- package/template/packages/shared/src/const/roles.id.ts +5 -0
- package/template/packages/shared/src/const/system.roles.id.ts +4 -0
- package/template/packages/shared/src/index.ts +1 -0
- package/template/packages/shared/tsconfig.json +10 -0
- package/template/packages/shared/tsup.config.ts +16 -0
- package/template/pnpm-workspace.yaml +3 -0
- package/template/tsconfig.base.json +62 -0
- package/template/tsconfig.json +12 -0
- package/template/turbo.json +88 -0
- package/template/versions.production.json +4 -0
|
@@ -0,0 +1,1591 @@
|
|
|
1
|
+
# Docker Setup Guide
|
|
2
|
+
|
|
3
|
+
Complete guide for running {{name}} with Docker Compose, including both development and production configurations.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Service Architecture](#service-architecture)
|
|
9
|
+
- [Prerequisites](#prerequisites)
|
|
10
|
+
- [Quick Start](#quick-start)
|
|
11
|
+
- [Development Mode](#development-mode)
|
|
12
|
+
- [Production Mode](#production-mode)
|
|
13
|
+
- [Deployment Strategies](#deployment-strategies)
|
|
14
|
+
- [Horizontal Scaling Guide](#horizontal-scaling-guide)
|
|
15
|
+
- [Architecture](#architecture)
|
|
16
|
+
- [Critical Configuration](#critical-configuration)
|
|
17
|
+
- [Environment Variables](#environment-variables)
|
|
18
|
+
- [Troubleshooting](#troubleshooting)
|
|
19
|
+
- [Advanced Topics](#advanced-topics)
|
|
20
|
+
|
|
21
|
+
## Overview
|
|
22
|
+
|
|
23
|
+
The {{name}} monorepo uses Docker Compose to run three application services:
|
|
24
|
+
|
|
25
|
+
- **API** (NestJS) - Backend API server on port 3400
|
|
26
|
+
- **Worker** (NestJS) - Background job processor
|
|
27
|
+
- **Web** (Next.js) - Frontend application on port 3401
|
|
28
|
+
|
|
29
|
+
**Infrastructure Services:** Neo4j, Redis, and MinIO are expected to run **externally** and are configured via environment variables in `.env`. They are NOT included in the docker-compose.yml by default.
|
|
30
|
+
|
|
31
|
+
## Service Architecture
|
|
32
|
+
|
|
33
|
+
{{name}} is designed as a microservices architecture with three independent services that can be deployed together or separately based on your scaling needs.
|
|
34
|
+
|
|
35
|
+
### Service Responsibilities
|
|
36
|
+
|
|
37
|
+
#### API Service (NestJS)
|
|
38
|
+
**What it does:**
|
|
39
|
+
- REST API endpoints for all business logic
|
|
40
|
+
- User authentication and authorization (JWT)
|
|
41
|
+
- Real-time database queries (Neo4j)
|
|
42
|
+
- Request validation and rate limiting
|
|
43
|
+
- CORS handling
|
|
44
|
+
- Caching frequently accessed data (Redis)
|
|
45
|
+
|
|
46
|
+
**When to scale:**
|
|
47
|
+
- High number of concurrent users
|
|
48
|
+
- Slow API response times (>200ms)
|
|
49
|
+
- CPU usage consistently >80%
|
|
50
|
+
- Many requests per second (>500 req/sec)
|
|
51
|
+
|
|
52
|
+
**Resource profile:** CPU-intensive, stateless (easy to scale horizontally)
|
|
53
|
+
|
|
54
|
+
#### Worker Service (NestJS)
|
|
55
|
+
**What it does:**
|
|
56
|
+
- Background job processing (BullMQ via Redis)
|
|
57
|
+
- Email sending (notifications, reports)
|
|
58
|
+
- Data processing and transformations
|
|
59
|
+
- Scheduled tasks (cron jobs)
|
|
60
|
+
- Heavy computations that shouldn't block API responses
|
|
61
|
+
- Bulk operations (imports, exports)
|
|
62
|
+
|
|
63
|
+
**When to scale:**
|
|
64
|
+
- Redis queue backlog growing
|
|
65
|
+
- Jobs taking longer to start
|
|
66
|
+
- Email delivery delays
|
|
67
|
+
- Background tasks timing out
|
|
68
|
+
|
|
69
|
+
**Resource profile:** Mixed workload, stateless, processes jobs from shared Redis queue
|
|
70
|
+
|
|
71
|
+
#### Web Service (Next.js)
|
|
72
|
+
**What it does:**
|
|
73
|
+
- Server-Side Rendering (SSR) for dynamic pages
|
|
74
|
+
- Static Site Generation (SSG) for cached pages
|
|
75
|
+
- Frontend React application
|
|
76
|
+
- Serving static assets (images, JS, CSS)
|
|
77
|
+
- API proxy for browser requests
|
|
78
|
+
|
|
79
|
+
**When to scale:**
|
|
80
|
+
- High website traffic
|
|
81
|
+
- Slow page load times
|
|
82
|
+
- Server timeouts during traffic spikes
|
|
83
|
+
- Geographic distribution needs
|
|
84
|
+
|
|
85
|
+
**Resource profile:** Memory-intensive (SSR), stateless (can be cached with CDN)
|
|
86
|
+
|
|
87
|
+
### Why Separate Services?
|
|
88
|
+
|
|
89
|
+
**Independent Scaling**
|
|
90
|
+
- Scale only the service experiencing load
|
|
91
|
+
- API can handle 1000s of requests while Workers process heavy jobs
|
|
92
|
+
- Web can scale for traffic without affecting API capacity
|
|
93
|
+
|
|
94
|
+
**Resource Optimization**
|
|
95
|
+
- API needs more CPU for request processing
|
|
96
|
+
- Workers need steady CPU for long-running jobs
|
|
97
|
+
- Web needs memory for SSR but can be cached with CDN
|
|
98
|
+
- Deploy services on appropriately-sized servers
|
|
99
|
+
|
|
100
|
+
**Fault Isolation**
|
|
101
|
+
- Worker crash doesn't affect API availability
|
|
102
|
+
- API issues don't block background job processing
|
|
103
|
+
- Web server problems don't impact API-to-API communication
|
|
104
|
+
|
|
105
|
+
**Geographic Distribution**
|
|
106
|
+
- Deploy Web servers closer to users (multiple regions)
|
|
107
|
+
- Keep API + Workers near database (low latency)
|
|
108
|
+
- Reduce costs by not replicating heavy backend services
|
|
109
|
+
|
|
110
|
+
**Cost Efficiency**
|
|
111
|
+
- Run fewer Workers during low-activity hours
|
|
112
|
+
- Scale API during business hours
|
|
113
|
+
- Keep Web instances minimal with CDN for static content
|
|
114
|
+
|
|
115
|
+
### Shared Infrastructure
|
|
116
|
+
|
|
117
|
+
All three services share access to:
|
|
118
|
+
- **Neo4j** (Database) - All services read/write data
|
|
119
|
+
- **Redis** (Cache & Queue) - API caches data, Workers consume job queue
|
|
120
|
+
- **MinIO/S3** (Storage) - All services store/retrieve files
|
|
121
|
+
|
|
122
|
+
This shared infrastructure must be:
|
|
123
|
+
- Highly available (avoid single point of failure)
|
|
124
|
+
- Network-accessible from all service instances
|
|
125
|
+
- Properly secured (firewalls, authentication)
|
|
126
|
+
|
|
127
|
+
## Prerequisites
|
|
128
|
+
|
|
129
|
+
1. **Docker Desktop** or **Docker Engine + Docker Compose**
|
|
130
|
+
2. **External Infrastructure** running:
|
|
131
|
+
- Neo4j (Graph Database)
|
|
132
|
+
- Redis (Cache & Queue)
|
|
133
|
+
- MinIO or S3 (Object Storage)
|
|
134
|
+
3. **Configured .env file** with all connection strings
|
|
135
|
+
|
|
136
|
+
## Quick Start
|
|
137
|
+
|
|
138
|
+
### 1. Configure Environment
|
|
139
|
+
|
|
140
|
+
Create/edit your `.env` file in the project root with these **REQUIRED** variables:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# API Configuration (REQUIRED)
|
|
144
|
+
API_PORT=3400
|
|
145
|
+
API_URL=http://localhost:3400/
|
|
146
|
+
|
|
147
|
+
# Web Configuration (REQUIRED)
|
|
148
|
+
PORT=3401
|
|
149
|
+
APP_URL=http://localhost:3401/
|
|
150
|
+
NEXT_PUBLIC_API_URL=http://localhost:3400/
|
|
151
|
+
NEXT_PUBLIC_ADDRESS=http://localhost:3401
|
|
152
|
+
|
|
153
|
+
# Database (REQUIRED)
|
|
154
|
+
NEO4J_URI=bolt://localhost:7687
|
|
155
|
+
NEO4J_USER=neo4j
|
|
156
|
+
NEO4J_PASSWORD=your-password
|
|
157
|
+
NEO4J_DATABASE=your-database
|
|
158
|
+
|
|
159
|
+
# Cache & Queue (REQUIRED)
|
|
160
|
+
REDIS_HOST=localhost
|
|
161
|
+
REDIS_PORT=6379
|
|
162
|
+
REDIS_PASSWORD=your-redis-password
|
|
163
|
+
|
|
164
|
+
# Object Storage (REQUIRED)
|
|
165
|
+
S3_ENDPOINT=your-endpoint
|
|
166
|
+
S3_BUCKET=your-bucket
|
|
167
|
+
S3_ACCESS_KEY_ID=your-access-key
|
|
168
|
+
S3_SECRET_ACCESS_KEY=your-secret-key
|
|
169
|
+
S3_REGION=your-region
|
|
170
|
+
|
|
171
|
+
# Authentication (REQUIRED)
|
|
172
|
+
JWT_SECRET=your-jwt-secret-min-32-chars
|
|
173
|
+
|
|
174
|
+
# VAPID for Web Push (REQUIRED)
|
|
175
|
+
VAPID_PUBLIC_KEY=your-public-key
|
|
176
|
+
VAPID_PRIVATE_KEY=your-private-key
|
|
177
|
+
NEXT_PUBLIC_VAPID_PUBLIC_KEY=your-public-key
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**Note:** See the [Environment Variables](#environment-variables) section below for complete reference including optional variables.
|
|
181
|
+
|
|
182
|
+
### 2. Build and Start
|
|
183
|
+
|
|
184
|
+
#### Option A: All Services (Monolithic - Recommended for Development)
|
|
185
|
+
|
|
186
|
+
Run all three services together on a single machine:
|
|
187
|
+
|
|
188
|
+
```bash
|
|
189
|
+
# Build dev-stage images (ensures development targets)
|
|
190
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml build --pull
|
|
191
|
+
|
|
192
|
+
# Start all services with development overrides (adds source + shared mounts)
|
|
193
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
|
|
194
|
+
|
|
195
|
+
# Or start in background
|
|
196
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d --build
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
**Use this when:**
|
|
200
|
+
- Local development
|
|
201
|
+
- Testing full stack
|
|
202
|
+
- Small deployments (<1000 users)
|
|
203
|
+
- Single server production
|
|
204
|
+
|
|
205
|
+
#### Option B: Individual Services (Distributed - Recommended for Production)
|
|
206
|
+
|
|
207
|
+
Run specific services for horizontal scaling and distributed deployment. Use only the base `docker-compose.yml` so the container keeps the compiled packages from the image:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
# API only
|
|
211
|
+
docker compose up --build api
|
|
212
|
+
|
|
213
|
+
# Worker only
|
|
214
|
+
docker compose up --build worker
|
|
215
|
+
|
|
216
|
+
# Web only
|
|
217
|
+
docker compose up --build web
|
|
218
|
+
|
|
219
|
+
# API + Worker (common for backend server)
|
|
220
|
+
docker compose up --build api worker
|
|
221
|
+
|
|
222
|
+
# Multiple instances (scale workers)
|
|
223
|
+
docker compose up --build --scale worker=3
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Use this when:**
|
|
227
|
+
- Scaling specific services based on load
|
|
228
|
+
- Multi-server deployment
|
|
229
|
+
- Independent service updates
|
|
230
|
+
- Resource optimization
|
|
231
|
+
|
|
232
|
+
### 3. Verify
|
|
233
|
+
|
|
234
|
+
- API: http://localhost:3400
|
|
235
|
+
- Web: http://localhost:3401
|
|
236
|
+
- Workers: Check logs with `docker compose logs worker`
|
|
237
|
+
|
|
238
|
+
## Development Mode
|
|
239
|
+
|
|
240
|
+
Development mode enables hot-reload for rapid development with live code changes. Combine the base compose file with `docker-compose.dev.yml` to add the necessary source and shared bind mounts.
|
|
241
|
+
|
|
242
|
+
### Features
|
|
243
|
+
|
|
244
|
+
- **Hot Reload**: Source code is mounted from host, changes reflect immediately
|
|
245
|
+
- **Development Server**: Runs `pnpm dev` with Turbopack
|
|
246
|
+
- **Verbose Logging**: Detailed error messages and stack traces
|
|
247
|
+
- **Source Maps**: Enabled for debugging
|
|
248
|
+
|
|
249
|
+
### Volume Mounts
|
|
250
|
+
|
|
251
|
+
The following directories are mounted for hot-reload:
|
|
252
|
+
|
|
253
|
+
These mounts are defined in `docker-compose.dev.yml`:
|
|
254
|
+
|
|
255
|
+
```yaml
|
|
256
|
+
# Web Service
|
|
257
|
+
./apps/web/src -> /app/apps/web/src
|
|
258
|
+
./apps/web/public -> /app/apps/web/public
|
|
259
|
+
./packages/shared -> /app/packages/shared
|
|
260
|
+
|
|
261
|
+
# API Service
|
|
262
|
+
./apps/api/src -> /app/apps/api/src
|
|
263
|
+
./packages/shared -> /app/packages/shared
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Commands
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
# Build development images
|
|
270
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml build --pull
|
|
271
|
+
|
|
272
|
+
# Start in development mode (adds dev overrides)
|
|
273
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
|
|
274
|
+
|
|
275
|
+
# View logs
|
|
276
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f web
|
|
277
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml logs -f api
|
|
278
|
+
|
|
279
|
+
# Stop services
|
|
280
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml down
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Code Changes
|
|
284
|
+
|
|
285
|
+
When running in development mode:
|
|
286
|
+
- ✅ Code changes in `src/` directories are picked up automatically
|
|
287
|
+
- ✅ No rebuild needed for most changes
|
|
288
|
+
- ⚠️ Changes to `package.json`, `Dockerfile`, or dependencies require rebuild:
|
|
289
|
+
```bash
|
|
290
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml down
|
|
291
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml build --no-cache
|
|
292
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Production Mode
|
|
296
|
+
|
|
297
|
+
Production mode creates optimized builds with minimal runtime footprint. The base compose file is now production-oriented by default (no override required).
|
|
298
|
+
|
|
299
|
+
### Features
|
|
300
|
+
|
|
301
|
+
- **Optimized Build**: Pre-built, minified production bundles
|
|
302
|
+
- **No Source Code**: Only compiled output included
|
|
303
|
+
- **Smaller Images**: No development dependencies
|
|
304
|
+
- **Production Runtime**: Runs `pnpm start` for Next.js, optimized NestJS build for API
|
|
305
|
+
|
|
306
|
+
### Build Process
|
|
307
|
+
|
|
308
|
+
The production build:
|
|
309
|
+
1. Installs all dependencies
|
|
310
|
+
2. Builds shared packages
|
|
311
|
+
3. Builds Next.js application with environment variables
|
|
312
|
+
4. Builds NestJS API with optimizations
|
|
313
|
+
5. Creates final image with only production dependencies and built output
|
|
314
|
+
|
|
315
|
+
### Commands
|
|
316
|
+
|
|
317
|
+
```bash
|
|
318
|
+
# Build production images (base compose only)
|
|
319
|
+
docker compose build --pull
|
|
320
|
+
|
|
321
|
+
# Start in production mode (do NOT include docker-compose.dev.yml)
|
|
322
|
+
docker compose up --build -d
|
|
323
|
+
|
|
324
|
+
# Check status
|
|
325
|
+
docker compose ps
|
|
326
|
+
|
|
327
|
+
# View logs
|
|
328
|
+
docker compose logs -f
|
|
329
|
+
|
|
330
|
+
# Stop services
|
|
331
|
+
docker compose down
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Important Notes
|
|
335
|
+
|
|
336
|
+
⚠️ **Volume Mounts**: No `node_modules` or `.next` volumes are mounted in production; everything ships inside the image to avoid dependency drift.
|
|
337
|
+
|
|
338
|
+
⚠️ **Environment Variables**: Next.js requires build-time environment variables. If you change `NEXT_PUBLIC_*` variables, you must rebuild:
|
|
339
|
+
```bash
|
|
340
|
+
docker compose build --no-cache web
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## Deployment Strategies
|
|
344
|
+
|
|
345
|
+
Choose your deployment strategy based on your traffic, budget, and scalability needs.
|
|
346
|
+
|
|
347
|
+
### Scenario 1: Development (Single Machine)
|
|
348
|
+
|
|
349
|
+
**Setup:** All services on local development machine
|
|
350
|
+
|
|
351
|
+
**Services:** API + Worker + Web (all running)
|
|
352
|
+
|
|
353
|
+
**Use case:**
|
|
354
|
+
- Local development and testing
|
|
355
|
+
- Feature development
|
|
356
|
+
- Debugging full stack
|
|
357
|
+
|
|
358
|
+
**Commands:**
|
|
359
|
+
```bash
|
|
360
|
+
docker compose up
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
**Pros:**
|
|
364
|
+
- Simple setup
|
|
365
|
+
- Easy debugging
|
|
366
|
+
- Fast iteration
|
|
367
|
+
|
|
368
|
+
**Cons:**
|
|
369
|
+
- Resource intensive on single machine
|
|
370
|
+
- Not representative of production architecture
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
### Scenario 2: Small Production (Single Server, <1000 Users)
|
|
375
|
+
|
|
376
|
+
**Setup:** All services on one production server
|
|
377
|
+
|
|
378
|
+
**Services:** API + Worker + Web
|
|
379
|
+
|
|
380
|
+
**Architecture:**
|
|
381
|
+
```
|
|
382
|
+
┌──────────────────────┐
|
|
383
|
+
│ Single Server │
|
|
384
|
+
│ │
|
|
385
|
+
│ ┌────────────────┐ │
|
|
386
|
+
Users ──────────────┼─►│ Web (3401) │ │
|
|
387
|
+
│ └────────┬───────┘ │
|
|
388
|
+
│ │ │
|
|
389
|
+
│ ┌────────▼───────┐ │
|
|
390
|
+
│ │ API (3400) │ │
|
|
391
|
+
│ └────────┬───────┘ │
|
|
392
|
+
│ │ │
|
|
393
|
+
│ ┌────────▼───────┐ │
|
|
394
|
+
│ │ Worker x1 │ │
|
|
395
|
+
│ └────────┬───────┘ │
|
|
396
|
+
└───────────┼──────────┘
|
|
397
|
+
│
|
|
398
|
+
┌───────────▼──────────┐
|
|
399
|
+
│ Neo4j + Redis + MinIO│
|
|
400
|
+
│ (External/Local) │
|
|
401
|
+
└──────────────────────┘
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**Commands:**
|
|
405
|
+
```bash
|
|
406
|
+
docker compose up -d
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**Pros:**
|
|
410
|
+
- Simple deployment
|
|
411
|
+
- Low infrastructure cost
|
|
412
|
+
- Easy to manage
|
|
413
|
+
|
|
414
|
+
**Cons:**
|
|
415
|
+
- Limited scalability
|
|
416
|
+
- Single point of failure
|
|
417
|
+
- Resource contention between services
|
|
418
|
+
|
|
419
|
+
**Recommended server:** 4 CPU, 8GB RAM
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
### Scenario 3: Medium Production (Multiple Servers, 1k-10k Users)
|
|
424
|
+
|
|
425
|
+
**Setup:** Backend and frontend on separate servers
|
|
426
|
+
|
|
427
|
+
**Services:**
|
|
428
|
+
- **Server 1:** API + Worker (2-3 worker instances)
|
|
429
|
+
- **Server 2:** Web (with CDN)
|
|
430
|
+
|
|
431
|
+
**Architecture:**
|
|
432
|
+
```
|
|
433
|
+
┌──────────────────┐
|
|
434
|
+
│ Load Balancer │
|
|
435
|
+
│ or Nginx │
|
|
436
|
+
└────────┬─────────┘
|
|
437
|
+
│
|
|
438
|
+
┌──────────────┼──────────────┐
|
|
439
|
+
│ │ │
|
|
440
|
+
┌────────▼────────┐ │ ┌───────▼────────┐
|
|
441
|
+
│ Server 1 │ │ │ Server 2 │
|
|
442
|
+
│ (Backend) │ │ │ (Frontend) │
|
|
443
|
+
│ │ │ │ │
|
|
444
|
+
│ ┌─────────────┐ │ │ │ ┌────────────┐ │
|
|
445
|
+
│ │ API (3400) │◄┼─────┼─────┼─│ Web (3401) │ │
|
|
446
|
+
│ └──────┬──────┘ │ │ │ └────────────┘ │
|
|
447
|
+
│ │ │ │ └────────────────┘
|
|
448
|
+
│ ┌──────▼──────┐ │ │
|
|
449
|
+
│ │ Worker x3 │ │ │
|
|
450
|
+
│ └──────┬──────┘ │ │
|
|
451
|
+
└────────┼────────┘ │
|
|
452
|
+
│ │
|
|
453
|
+
┌────────▼──────────────▼──────────┐
|
|
454
|
+
│ Neo4j + Redis + MinIO │
|
|
455
|
+
│ (Managed/External Services) │
|
|
456
|
+
└──────────────────────────────────┘
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
**Server 1 Commands:**
|
|
460
|
+
```bash
|
|
461
|
+
# Backend server
|
|
462
|
+
docker compose up api --scale worker=3 -d
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
**Server 2 Commands:**
|
|
466
|
+
```bash
|
|
467
|
+
# Frontend server (configure API_INTERNAL_URL to point to Server 1)
|
|
468
|
+
docker compose up web -d
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
**Pros:**
|
|
472
|
+
- Better resource allocation
|
|
473
|
+
- Independent scaling
|
|
474
|
+
- Web can use CDN
|
|
475
|
+
- Backend stays near database
|
|
476
|
+
|
|
477
|
+
**Cons:**
|
|
478
|
+
- More complex networking
|
|
479
|
+
- Multiple servers to manage
|
|
480
|
+
- Higher infrastructure cost
|
|
481
|
+
|
|
482
|
+
**Recommended:**
|
|
483
|
+
- Server 1: 4 CPU, 8GB RAM (Backend)
|
|
484
|
+
- Server 2: 2 CPU, 4GB RAM (Frontend + CDN)
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
### Scenario 4: Large Production (High Availability, 10k+ Users)
|
|
489
|
+
|
|
490
|
+
**Setup:** Multiple instances of each service with load balancing
|
|
491
|
+
|
|
492
|
+
**Services:**
|
|
493
|
+
- **API Cluster:** 3+ API instances behind load balancer
|
|
494
|
+
- **Worker Pool:** 5+ workers for parallel processing
|
|
495
|
+
- **Web Cluster:** 2+ web instances with CDN
|
|
496
|
+
- **Infrastructure:** Managed Neo4j cluster, Redis cluster, S3
|
|
497
|
+
|
|
498
|
+
**Architecture:**
|
|
499
|
+
```
|
|
500
|
+
┌──────────────────┐
|
|
501
|
+
│ CDN (Cloudflare)│
|
|
502
|
+
└────────┬─────────┘
|
|
503
|
+
│
|
|
504
|
+
┌────────▼─────────┐
|
|
505
|
+
│ Load Balancer │
|
|
506
|
+
│ (HAProxy/Nginx) │
|
|
507
|
+
└────────┬─────────┘
|
|
508
|
+
│
|
|
509
|
+
┌───────────────┼───────────────┐
|
|
510
|
+
│ │ │
|
|
511
|
+
┌───────▼──────┐ ┌────▼──────┐ ┌────▼──────┐
|
|
512
|
+
│ Web-1 │ │ Web-2 │ │ Web-3 │
|
|
513
|
+
│ (3401) │ │ (3401) │ │ (3401) │
|
|
514
|
+
└───────┬──────┘ └────┬──────┘ └────┬──────┘
|
|
515
|
+
│ │ │
|
|
516
|
+
└───────────────┼───────────────┘
|
|
517
|
+
│
|
|
518
|
+
┌────────▼─────────┐
|
|
519
|
+
│ API Load Bal. │
|
|
520
|
+
└────────┬─────────┘
|
|
521
|
+
│
|
|
522
|
+
┌───────────────┼───────────────┐
|
|
523
|
+
│ │ │
|
|
524
|
+
┌───────▼──────┐ ┌────▼──────┐ ┌────▼──────┐
|
|
525
|
+
│ API-1 │ │ API-2 │ │ API-3 │
|
|
526
|
+
│ (3400) │ │ (3400) │ │ (3400) │
|
|
527
|
+
└───────┬──────┘ └────┬──────┘ └────┬──────┘
|
|
528
|
+
│ │ │
|
|
529
|
+
└───────────────┼───────────────┘
|
|
530
|
+
│
|
|
531
|
+
┌───────────────────────┼───────────────┐
|
|
532
|
+
│ │ │
|
|
533
|
+
┌────▼─────┐ ┌──────────▼──────┐ ┌───────▼──────┐
|
|
534
|
+
│Worker x5 │ │Worker Pool x5 │ │Worker Pool x5│
|
|
535
|
+
│ (Srv 1) │ │ (Srv 2) │ │ (Srv 3) │
|
|
536
|
+
└────┬─────┘ └──────────┬──────┘ └───────┬──────┘
|
|
537
|
+
│ │ │
|
|
538
|
+
└───────────────────┼──────────────────┘
|
|
539
|
+
│
|
|
540
|
+
┌───────────────────▼───────────────────┐
|
|
541
|
+
│ Managed Infrastructure │
|
|
542
|
+
│ ┌──────────┐ ┌───────┐ ┌────────┐ │
|
|
543
|
+
│ │Neo4j │ │ Redis │ │ S3 │ │
|
|
544
|
+
│ │ Cluster │ │Cluster│ │ Bucket │ │
|
|
545
|
+
│ └──────────┘ └───────┘ └────────┘ │
|
|
546
|
+
└────────────────────────────────────────┘
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
**Setup Commands:**
|
|
550
|
+
```bash
|
|
551
|
+
# Web servers (Server 1-3)
|
|
552
|
+
docker compose up web -d
|
|
553
|
+
|
|
554
|
+
# API servers (Server 4-6)
|
|
555
|
+
docker compose up api -d
|
|
556
|
+
|
|
557
|
+
# Worker servers (Server 7-9)
|
|
558
|
+
docker compose up --scale worker=5 -d
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
**Pros:**
|
|
562
|
+
- High availability (no single point of failure)
|
|
563
|
+
- Horizontal scalability
|
|
564
|
+
- Geographic distribution possible
|
|
565
|
+
- Fault tolerance
|
|
566
|
+
|
|
567
|
+
**Cons:**
|
|
568
|
+
- Complex setup and management
|
|
569
|
+
- Higher costs
|
|
570
|
+
- Requires load balancer configuration
|
|
571
|
+
- More monitoring needed
|
|
572
|
+
|
|
573
|
+
**Recommended:**
|
|
574
|
+
- Web servers: 2 CPU, 4GB RAM each
|
|
575
|
+
- API servers: 4 CPU, 8GB RAM each
|
|
576
|
+
- Worker servers: 2 CPU, 4GB RAM each
|
|
577
|
+
- Use managed database services
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
### Deployment Decision Matrix
|
|
582
|
+
|
|
583
|
+
| Users | Requests/sec | Jobs/day | Recommended Setup | Estimated Cost |
|
|
584
|
+
|------------|--------------|----------|-----------------------------|-----------------|
|
|
585
|
+
| <1,000 | <100 | <1,000 | Scenario 1: Single Server | $50-100/month |
|
|
586
|
+
| 1k-10k | 100-500 | 1k-10k | Scenario 2: 2 Servers | $200-400/month |
|
|
587
|
+
| 10k-50k | 500-2000 | 10k-100k | Scenario 3: Clustered | $800-1500/month |
|
|
588
|
+
| 50k+ | 2000+ | 100k+ | Scenario 4: Full HA | $2000+/month |
|
|
589
|
+
|
|
590
|
+
## Horizontal Scaling Guide
|
|
591
|
+
|
|
592
|
+
### Understanding Service Load Patterns
|
|
593
|
+
|
|
594
|
+
Each service has different scaling characteristics:
|
|
595
|
+
|
|
596
|
+
#### API Service Scaling
|
|
597
|
+
|
|
598
|
+
**Bottleneck Indicators:**
|
|
599
|
+
- Response time >200ms
|
|
600
|
+
- CPU usage >80%
|
|
601
|
+
- High request queue
|
|
602
|
+
- Connection timeouts
|
|
603
|
+
|
|
604
|
+
**Scaling Strategy:**
|
|
605
|
+
```bash
|
|
606
|
+
# Add more API instances behind load balancer
|
|
607
|
+
# Server 1
|
|
608
|
+
docker compose up api -d
|
|
609
|
+
|
|
610
|
+
# Server 2
|
|
611
|
+
docker compose up api -d
|
|
612
|
+
|
|
613
|
+
# Server 3
|
|
614
|
+
docker compose up api -d
|
|
615
|
+
|
|
616
|
+
# Configure load balancer to distribute across all three
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
**How it works:**
|
|
620
|
+
- Each API instance is stateless
|
|
621
|
+
- All connect to same Neo4j/Redis/MinIO
|
|
622
|
+
- Load balancer distributes requests (round-robin, least-connections, etc.)
|
|
623
|
+
- Share same JWT_SECRET for authentication
|
|
624
|
+
- No session stickiness needed
|
|
625
|
+
|
|
626
|
+
**When to scale:**
|
|
627
|
+
- CPU consistently >80%
|
|
628
|
+
- Response times degrading
|
|
629
|
+
- >500 requests per second per instance
|
|
630
|
+
|
|
631
|
+
---
|
|
632
|
+
|
|
633
|
+
#### Worker Service Scaling
|
|
634
|
+
|
|
635
|
+
**Bottleneck Indicators:**
|
|
636
|
+
- Redis queue length growing (>100 jobs)
|
|
637
|
+
- Job wait time increasing
|
|
638
|
+
- Jobs timing out
|
|
639
|
+
- Background tasks delayed
|
|
640
|
+
|
|
641
|
+
**Scaling Strategy:**
|
|
642
|
+
```bash
|
|
643
|
+
# Scale workers on same server
|
|
644
|
+
docker compose up --scale worker=5 -d
|
|
645
|
+
|
|
646
|
+
# Or distribute across multiple servers
|
|
647
|
+
# Server 1
|
|
648
|
+
docker compose up worker -d
|
|
649
|
+
|
|
650
|
+
# Server 2
|
|
651
|
+
docker compose up worker -d
|
|
652
|
+
|
|
653
|
+
# All workers automatically share the Redis queue
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
**How it works:**
|
|
657
|
+
- BullMQ (via Redis) automatically distributes jobs
|
|
658
|
+
- Workers pull jobs from shared queue
|
|
659
|
+
- No coordination needed between workers
|
|
660
|
+
- Can run different numbers of workers per server
|
|
661
|
+
- Jobs processed in parallel
|
|
662
|
+
|
|
663
|
+
**When to scale:**
|
|
664
|
+
- Queue length >50 jobs consistently
|
|
665
|
+
- Job processing time >5 minutes average
|
|
666
|
+
- Email/notification delays
|
|
667
|
+
- Scheduled tasks missing deadlines
|
|
668
|
+
|
|
669
|
+
---
|
|
670
|
+
|
|
671
|
+
#### Web Service Scaling
|
|
672
|
+
|
|
673
|
+
**Bottleneck Indicators:**
|
|
674
|
+
- Slow page loads (>2 seconds)
|
|
675
|
+
- High memory usage (>80%)
|
|
676
|
+
- Server errors during traffic spikes
|
|
677
|
+
- Geographic latency issues
|
|
678
|
+
|
|
679
|
+
**Scaling Strategy:**
|
|
680
|
+
```bash
|
|
681
|
+
# Add web instances behind load balancer
|
|
682
|
+
# Server 1 (US East)
|
|
683
|
+
docker compose up web -d
|
|
684
|
+
|
|
685
|
+
# Server 2 (US West)
|
|
686
|
+
docker compose up web -d
|
|
687
|
+
|
|
688
|
+
# Server 3 (EU)
|
|
689
|
+
docker compose up web -d
|
|
690
|
+
|
|
691
|
+
# Use CDN (Cloudflare/CloudFront) in front
|
|
692
|
+
```
|
|
693
|
+
|
|
694
|
+
**How it works:**
|
|
695
|
+
- Each web instance is stateless
|
|
696
|
+
- All call same API endpoint
|
|
697
|
+
- CDN caches static assets
|
|
698
|
+
- Load balancer with optional sticky sessions
|
|
699
|
+
- Geographic distribution reduces latency
|
|
700
|
+
|
|
701
|
+
**When to scale:**
|
|
702
|
+
- Memory usage >80%
|
|
703
|
+
- Page load time >1 second
|
|
704
|
+
- >100 concurrent users per instance
|
|
705
|
+
- Geographic distribution needed
|
|
706
|
+
|
|
707
|
+
---
|
|
708
|
+
|
|
709
|
+
### Capacity Planning Table
|
|
710
|
+
|
|
711
|
+
| Service | Per Instance Capacity | When to Add Instance | Resource per Instance |
|
|
712
|
+
|---------|----------------------|---------------------|----------------------|
|
|
713
|
+
| API | ~500 req/sec | CPU >80% or latency >200ms | 2 CPU, 4GB RAM |
|
|
714
|
+
| Worker | ~10-50 jobs/min | Queue >100 or delay >5min | 1 CPU, 2GB RAM |
|
|
715
|
+
| Web | ~100-200 concurrent users | Memory >80% or load time >1s | 2 CPU, 4GB RAM |
|
|
716
|
+
|
|
717
|
+
### Multi-Server Configuration Examples
|
|
718
|
+
|
|
719
|
+
#### Backend Server (API + Workers)
|
|
720
|
+
|
|
721
|
+
```bash
|
|
722
|
+
# Server 1: backend.example.com
|
|
723
|
+
# .env configuration
|
|
724
|
+
|
|
725
|
+
# Point to actual infrastructure
|
|
726
|
+
NEO4J_URI=bolt://neo4j-cluster.internal:7687
|
|
727
|
+
REDIS_HOST=redis-cluster.internal
|
|
728
|
+
S3_ENDPOINT=https://s3.amazonaws.com
|
|
729
|
+
|
|
730
|
+
# API configuration
|
|
731
|
+
API_PORT=3400
|
|
732
|
+
API_URL=https://api.example.com/
|
|
733
|
+
|
|
734
|
+
# Start backend services (production stages baked into image)
|
|
735
|
+
docker compose up --build api --scale worker=3 -d
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
#### Frontend Server (Web Only)
|
|
739
|
+
|
|
740
|
+
```bash
|
|
741
|
+
# Server 2: web.example.com
|
|
742
|
+
# .env configuration
|
|
743
|
+
|
|
744
|
+
# Point to backend API
|
|
745
|
+
NEXT_PUBLIC_API_URL=https://api.example.com/
|
|
746
|
+
API_INTERNAL_URL=https://api.example.com/ # External since on different server
|
|
747
|
+
|
|
748
|
+
# Same infrastructure (for session storage, file uploads)
|
|
749
|
+
NEO4J_URI=bolt://neo4j-cluster.internal:7687
|
|
750
|
+
REDIS_HOST=redis-cluster.internal
|
|
751
|
+
S3_ENDPOINT=https://s3.amazonaws.com
|
|
752
|
+
|
|
753
|
+
# Web configuration
|
|
754
|
+
PORT=3401
|
|
755
|
+
APP_URL=https://app.example.com/
|
|
756
|
+
NEXT_PUBLIC_ADDRESS=https://app.example.com
|
|
757
|
+
|
|
758
|
+
# Google API key
|
|
759
|
+
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY="YOUR PLACES(New) API KEY"
|
|
760
|
+
|
|
761
|
+
# Start web service
|
|
762
|
+
docker compose up --build web -d
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
#### Load Balancer Configuration
|
|
766
|
+
|
|
767
|
+
**Nginx Load Balancer for API:**
|
|
768
|
+
```nginx
|
|
769
|
+
upstream api_backend {
|
|
770
|
+
least_conn; # Send to server with fewest connections
|
|
771
|
+
server api-server-1.internal:3400;
|
|
772
|
+
server api-server-2.internal:3400;
|
|
773
|
+
server api-server-3.internal:3400;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
server {
|
|
777
|
+
listen 443 ssl http2;
|
|
778
|
+
server_name api.example.com;
|
|
779
|
+
|
|
780
|
+
ssl_certificate /etc/ssl/api.example.com.crt;
|
|
781
|
+
ssl_certificate_key /etc/ssl/api.example.com.key;
|
|
782
|
+
|
|
783
|
+
location / {
|
|
784
|
+
proxy_pass http://api_backend;
|
|
785
|
+
proxy_set_header Host $host;
|
|
786
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
787
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
788
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
789
|
+
|
|
790
|
+
# Timeouts
|
|
791
|
+
proxy_connect_timeout 30s;
|
|
792
|
+
proxy_send_timeout 30s;
|
|
793
|
+
proxy_read_timeout 30s;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
```
|
|
797
|
+
|
|
798
|
+
**Nginx Load Balancer for Web:**
|
|
799
|
+
```nginx
|
|
800
|
+
upstream web_backend {
|
|
801
|
+
# ip_hash for sticky sessions (optional - depends on your SSR needs)
|
|
802
|
+
server web-server-1.internal:3401;
|
|
803
|
+
server web-server-2.internal:3401;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
server {
|
|
807
|
+
listen 443 ssl http2;
|
|
808
|
+
server_name app.example.com;
|
|
809
|
+
|
|
810
|
+
ssl_certificate /etc/ssl/app.example.com.crt;
|
|
811
|
+
ssl_certificate_key /etc/ssl/app.example.com.key;
|
|
812
|
+
|
|
813
|
+
location / {
|
|
814
|
+
proxy_pass http://web_backend;
|
|
815
|
+
proxy_set_header Host $host;
|
|
816
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
817
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
818
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
819
|
+
|
|
820
|
+
# WebSocket support (if needed)
|
|
821
|
+
proxy_http_version 1.1;
|
|
822
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
823
|
+
proxy_set_header Connection "upgrade";
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
```
|
|
827
|
+
|
|
828
|
+
### Monitoring and Auto-Scaling
|
|
829
|
+
|
|
830
|
+
**Key Metrics to Monitor:**
|
|
831
|
+
|
|
832
|
+
**API:**
|
|
833
|
+
- Request rate (req/sec)
|
|
834
|
+
- Response time (p50, p95, p99)
|
|
835
|
+
- Error rate (5xx responses)
|
|
836
|
+
- CPU and memory usage
|
|
837
|
+
|
|
838
|
+
**Worker:**
|
|
839
|
+
- Queue length (Redis)
|
|
840
|
+
- Job processing time
|
|
841
|
+
- Failed job count
|
|
842
|
+
- Active workers
|
|
843
|
+
|
|
844
|
+
**Web:**
|
|
845
|
+
- Page load time
|
|
846
|
+
- Memory usage
|
|
847
|
+
- Concurrent connections
|
|
848
|
+
- Cache hit rate
|
|
849
|
+
|
|
850
|
+
**Auto-scaling Triggers:**
|
|
851
|
+
- Scale up API when: CPU >75% for 5 minutes
|
|
852
|
+
- Scale up Workers when: Queue length >50 for 10 minutes
|
|
853
|
+
- Scale up Web when: Memory >75% for 5 minutes
|
|
854
|
+
- Scale down: When metrics drop below 40% for 15 minutes
|
|
855
|
+
|
|
856
|
+
## Architecture
|
|
857
|
+
|
|
858
|
+
```
|
|
859
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
860
|
+
│ Docker Compose Service │
|
|
861
|
+
│ │
|
|
862
|
+
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
|
863
|
+
│ │ API │◄────────│ Worker │ │ Web │ │
|
|
864
|
+
│ │ (NestJS) │ │ (NestJS) │ │ (Next.js)│ │
|
|
865
|
+
│ │ Port 3400│ │ │ │ Port 3401│ │
|
|
866
|
+
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
|
867
|
+
│ │ │ │ │
|
|
868
|
+
│ │ | │ │
|
|
869
|
+
│ │ │ │ │
|
|
870
|
+
└───────┼────────────────────┼─────────────────────┼──────────────┘
|
|
871
|
+
│ │ │
|
|
872
|
+
└────────────────────┴─────────────────────┘
|
|
873
|
+
│
|
|
874
|
+
┌────────────────────┴────────────────────┐
|
|
875
|
+
│ | │
|
|
876
|
+
┌────▼────┐ ┌──────▼────┐ ┌────▼────┐
|
|
877
|
+
│ Neo4j │ │ Redis │ │ MinIO │
|
|
878
|
+
│Database │ │Cache/Queue│ │ Storage │
|
|
879
|
+
│External │ │ External │ │External │
|
|
880
|
+
└─────────┘ └───────────┘ └─────────┘
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### Request Flow
|
|
884
|
+
|
|
885
|
+
**Browser Request (Client-Side):**
|
|
886
|
+
```
|
|
887
|
+
Browser → http://localhost:3401 → Web Container → http://localhost:3400 → API Container
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
**Server-Side Rendering (SSR):**
|
|
891
|
+
```
|
|
892
|
+
Web Container → http://api:3400 (internal Docker network) → API Container
|
|
893
|
+
```
|
|
894
|
+
|
|
895
|
+
## Docker Build Architecture
|
|
896
|
+
|
|
897
|
+
### Unified Dockerfile Strategy
|
|
898
|
+
|
|
899
|
+
{{name}} uses a **unified Dockerfile** ([Dockerfile](Dockerfile)) that builds all services from a single file. This approach provides significant benefits over separate Dockerfiles:
|
|
900
|
+
|
|
901
|
+
**Benefits:**
|
|
902
|
+
- **Shared Package Built Once**: The `@{{name}}/shared` package is built in a single stage and reused by both API and Web services
|
|
903
|
+
- **Faster CI/CD**: When building multiple services, Docker reuses cached layers from the shared build stages
|
|
904
|
+
- **Consistent Dependencies**: All services use the exact same base image and dependency versions
|
|
905
|
+
- **Reduced Maintenance**: Single source of truth for build configuration
|
|
906
|
+
|
|
907
|
+
**Build Stage Flow:**
|
|
908
|
+
```
|
|
909
|
+
base
|
|
910
|
+
↓
|
|
911
|
+
workspace-deps (install all dependencies)
|
|
912
|
+
↓
|
|
913
|
+
shared-builder (build @{{name}}/shared ONCE)
|
|
914
|
+
├→ api-builder → api-production
|
|
915
|
+
└→ web-builder → web-production
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
**Previous Problem:**
|
|
919
|
+
Before the unified approach, each Dockerfile independently built the shared package:
|
|
920
|
+
- API Dockerfile: Built shared package
|
|
921
|
+
- Web Dockerfile: Built shared package again (duplicate work!)
|
|
922
|
+
|
|
923
|
+
As the shared package grows with more utilities, types, and constants, this duplication becomes increasingly wasteful.
|
|
924
|
+
|
|
925
|
+
**Build Targets:**
|
|
926
|
+
- `api-development`: API with hot-reload for development
|
|
927
|
+
- `api-production`: Optimized API for production
|
|
928
|
+
- `web-development`: Web with hot-reload for development
|
|
929
|
+
- `web-production`: Optimized Web for production
|
|
930
|
+
|
|
931
|
+
**Build Commands:**
|
|
932
|
+
```bash
|
|
933
|
+
# Build all services (shared package built once, reused)
|
|
934
|
+
docker compose build
|
|
935
|
+
|
|
936
|
+
# Build specific service (still benefits from shared caching)
|
|
937
|
+
docker compose build api
|
|
938
|
+
docker compose build web
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
### Shared Package (@{{name}}/shared)
|
|
942
|
+
|
|
943
|
+
The shared package contains common code used by both frontend and backend:
|
|
944
|
+
- TypeScript types and interfaces
|
|
945
|
+
- Enums and constants
|
|
946
|
+
- Utility functions
|
|
947
|
+
- Validation schemas
|
|
948
|
+
|
|
949
|
+
**Development Mode:**
|
|
950
|
+
- Source files are mounted via volumes ([docker-compose.dev.yml](docker-compose.dev.yml))
|
|
951
|
+
- TypeScript path mapping resolves to source: `./packages/shared/src`
|
|
952
|
+
- Changes to shared package require container restart
|
|
953
|
+
- Hot-reload works for app-specific code (API/Web src directories)
|
|
954
|
+
|
|
955
|
+
**Production Mode:**
|
|
956
|
+
- Shared package is built during Docker build
|
|
957
|
+
- Compiled output is included in final images
|
|
958
|
+
- No source files in production containers
|
|
959
|
+
|
|
960
|
+
**Import Strategy:**
|
|
961
|
+
```typescript
|
|
962
|
+
// Both API and Web import from the package
|
|
963
|
+
import { EquipmentStatus } from '@{{name}}/shared';
|
|
964
|
+
|
|
965
|
+
// TypeScript resolves via tsconfig paths during development
|
|
966
|
+
// Runtime uses built package.json main field in production
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
### .dockerignore Explanation
|
|
970
|
+
|
|
971
|
+
The [.dockerignore](.dockerignore) file excludes certain directories from the Docker build context:
|
|
972
|
+
|
|
973
|
+
```dockerignore
|
|
974
|
+
dist/
|
|
975
|
+
**/dist/
|
|
976
|
+
node_modules/
|
|
977
|
+
**/node_modules/
|
|
978
|
+
.next/
|
|
979
|
+
**/.next/
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
**Why this is correct:**
|
|
983
|
+
- ✅ `dist/` folders are **created during the Docker build**, not copied from host
|
|
984
|
+
- ✅ Multi-stage builds preserve `dist/` between stages (COPY --from=builder)
|
|
985
|
+
- ✅ Excluding these from the initial context reduces build context size
|
|
986
|
+
- ✅ Prevents stale local builds from interfering with container builds
|
|
987
|
+
|
|
988
|
+
**What gets excluded:**
|
|
989
|
+
- Host `node_modules/` (will be rebuilt inside container)
|
|
990
|
+
- Host `dist/` folders (will be built fresh inside container)
|
|
991
|
+
- Host `.next/` build output (rebuilt during container build)
|
|
992
|
+
|
|
993
|
+
**What gets preserved:**
|
|
994
|
+
- `dist/` folders created inside Docker during build stages
|
|
995
|
+
- COPY commands between stages work regardless of .dockerignore
|
|
996
|
+
|
|
997
|
+
### Health Checks
|
|
998
|
+
|
|
999
|
+
All services have health checks configured to ensure proper orchestration:
|
|
1000
|
+
|
|
1001
|
+
**API Service:**
|
|
1002
|
+
```yaml
|
|
1003
|
+
healthcheck:
|
|
1004
|
+
test: ["CMD", "node", "-e", "require('http').get({host:'localhost',port:process.env.API_PORT||3400,path:'/version'},(r)=>process.exit(r.statusCode>=200&&r.statusCode<500?0:1))"]
|
|
1005
|
+
interval: 10s
|
|
1006
|
+
timeout: 5s
|
|
1007
|
+
retries: 3
|
|
1008
|
+
start_period: 40s
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
**Web Service:**
|
|
1012
|
+
```yaml
|
|
1013
|
+
healthcheck:
|
|
1014
|
+
test: ["CMD", "node", "-e", "require('http').get({host:'localhost',port:process.env.PORT||3401,path:'/'},(r)=>process.exit(r.statusCode>=200&&r.statusCode<500?0:1))"]
|
|
1015
|
+
interval: 10s
|
|
1016
|
+
timeout: 5s
|
|
1017
|
+
retries: 3
|
|
1018
|
+
start_period: 40s
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
**Worker Service:**
|
|
1022
|
+
```yaml
|
|
1023
|
+
healthcheck:
|
|
1024
|
+
test: ["CMD", "pgrep", "-f", "node.*dist/main.*worker"]
|
|
1025
|
+
interval: 10s
|
|
1026
|
+
timeout: 5s
|
|
1027
|
+
retries: 3
|
|
1028
|
+
start_period: 30s
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
**Benefits:**
|
|
1032
|
+
- Docker knows when services are actually ready (not just running)
|
|
1033
|
+
- `docker compose ps` shows health status
|
|
1034
|
+
- Orchestration tools (Kubernetes, Coolify) can use health checks for:
|
|
1035
|
+
- Rolling updates
|
|
1036
|
+
- Load balancer registration
|
|
1037
|
+
- Automatic restarts
|
|
1038
|
+
- Readiness probes
|
|
1039
|
+
|
|
1040
|
+
**Checking Health:**
|
|
1041
|
+
```bash
|
|
1042
|
+
# View health status
|
|
1043
|
+
docker compose ps
|
|
1044
|
+
|
|
1045
|
+
# Should show: (healthy) next to running services
|
|
1046
|
+
```
|
|
1047
|
+
|
|
1048
|
+
### Runtime Dependencies
|
|
1049
|
+
|
|
1050
|
+
**Image Processing Libraries:**
|
|
1051
|
+
Both API and Web services include image processing dependencies:
|
|
1052
|
+
|
|
1053
|
+
```dockerfile
|
|
1054
|
+
RUN apk add --no-cache cairo jpeg pango giflib libwebp
|
|
1055
|
+
```
|
|
1056
|
+
|
|
1057
|
+
**Why both need them:**
|
|
1058
|
+
- **API**: Backend image processing, file uploads, document generation
|
|
1059
|
+
- **Web**: Next.js Image Optimization (sharp) for server-side image resizing
|
|
1060
|
+
|
|
1061
|
+
**Next.js Image Optimization:**
|
|
1062
|
+
Next.js uses `sharp` at runtime for optimizing images during server-side rendering. Without these dependencies, image optimization would fail with:
|
|
1063
|
+
```
|
|
1064
|
+
Error: Cannot find module 'sharp'
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
**Size Impact:**
|
|
1068
|
+
- Combined size: ~15-20MB per image
|
|
1069
|
+
- Required for production functionality
|
|
1070
|
+
- Cannot be removed without breaking features
|
|
1071
|
+
|
|
1072
|
+
## Critical Configuration
|
|
1073
|
+
|
|
1074
|
+
### Docker Networking for Next.js SSR
|
|
1075
|
+
|
|
1076
|
+
**THE PROBLEM:**
|
|
1077
|
+
When Next.js performs server-side rendering inside a Docker container, `localhost` refers to the web container itself, NOT the API container.
|
|
1078
|
+
|
|
1079
|
+
**THE SOLUTION:**
|
|
1080
|
+
The configuration uses different URLs for client-side vs server-side requests:
|
|
1081
|
+
|
|
1082
|
+
```bash
|
|
1083
|
+
# In .env
|
|
1084
|
+
NEXT_PUBLIC_API_URL=http://localhost:3400/ # Used by browser
|
|
1085
|
+
API_INTERNAL_URL=http://api:3400/ # Used by SSR (defaults to this)
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
**How It Works:**
|
|
1089
|
+
- `apps/web/src/config/env.ts` checks if code is running server-side (`typeof window === 'undefined'`)
|
|
1090
|
+
- If server-side: Uses `API_INTERNAL_URL` (Docker service name `api`)
|
|
1091
|
+
- If client-side: Uses `NEXT_PUBLIC_API_URL` (localhost for browser)
|
|
1092
|
+
|
|
1093
|
+
### ⚠️ CRITICAL: API_INTERNAL_URL for Multi-Server Deployments
|
|
1094
|
+
|
|
1095
|
+
**When services run on the SAME server (single docker-compose):**
|
|
1096
|
+
```bash
|
|
1097
|
+
API_INTERNAL_URL=http://api:3400/ # Docker service name works
|
|
1098
|
+
```
|
|
1099
|
+
The web container can reach the API container via Docker's internal network using the service name.
|
|
1100
|
+
|
|
1101
|
+
**When services run on DIFFERENT servers (distributed):**
|
|
1102
|
+
```bash
|
|
1103
|
+
# WRONG - This will NOT work:
|
|
1104
|
+
API_INTERNAL_URL=http://api:3400/ # Docker service name doesn't exist across servers
|
|
1105
|
+
|
|
1106
|
+
# CORRECT - Use external URL:
|
|
1107
|
+
API_INTERNAL_URL=https://api.example.com/ # Actual API server URL
|
|
1108
|
+
# OR
|
|
1109
|
+
API_INTERNAL_URL=http://10.0.1.50:3400/ # Internal IP address
|
|
1110
|
+
```
|
|
1111
|
+
|
|
1112
|
+
**Why This Matters:**
|
|
1113
|
+
- SSR requests from Web service MUST be able to reach the API
|
|
1114
|
+
- If Web is on Server A and API is on Server B, they're on different Docker networks
|
|
1115
|
+
- Docker service names (`api`) only work within the same Docker network
|
|
1116
|
+
- You MUST use the external URL or internal IP address
|
|
1117
|
+
|
|
1118
|
+
**Testing Multi-Server API_INTERNAL_URL:**
|
|
1119
|
+
```bash
|
|
1120
|
+
# From your web server, test if Web container can reach API
|
|
1121
|
+
docker compose exec web wget -O- --timeout=5 $API_INTERNAL_URL
|
|
1122
|
+
|
|
1123
|
+
# Should return API response, not timeout or connection refused
|
|
1124
|
+
# If it fails, your API_INTERNAL_URL is wrong
|
|
1125
|
+
```
|
|
1126
|
+
|
|
1127
|
+
**Common Mistakes:**
|
|
1128
|
+
- ❌ Using `http://localhost:3400/` - wrong, localhost is the web container
|
|
1129
|
+
- ❌ Using `http://api:3400/` when services are on different servers
|
|
1130
|
+
- ✅ Using `https://api.example.com/` - correct for distributed
|
|
1131
|
+
- ✅ Using `http://10.0.1.50:3400/` - correct for same network, different servers
|
|
1132
|
+
|
|
1133
|
+
**If you get 500 errors or timeouts on page load:**
|
|
1134
|
+
1. Verify `API_INTERNAL_URL` is correctly configured for your deployment
|
|
1135
|
+
2. Check web container can reach API: `docker compose exec web wget -O- $API_INTERNAL_URL`
|
|
1136
|
+
3. For multi-server: Ensure firewall rules allow web server to reach API server
|
|
1137
|
+
4. Check both containers have network connectivity to shared infrastructure (Neo4j, Redis, MinIO)
|
|
1138
|
+
|
|
1139
|
+
### Build Targets
|
|
1140
|
+
|
|
1141
|
+
The compose workflow now selects build targets automatically:
|
|
1142
|
+
|
|
1143
|
+
- **Base file (`docker-compose.yml`)** builds the `production` stage of each Dockerfile. Dependencies are baked into the image and the containers run the compiled output (`node dist/...`, `pnpm ... start`).
|
|
1144
|
+
- **Development override (`docker-compose.dev.yml`)** switches all services to the `development` stage when layered with the base file. The images include dev dependencies and expose hot-reload commands (`pnpm ... dev`, `nodemon`).
|
|
1145
|
+
|
|
1146
|
+
Switch between modes by including or omitting the dev override file, and always rebuild (`--build`) when you change modes so Docker picks the correct stage.
|
|
1147
|
+
|
|
1148
|
+
### Volume Mounting Strategy
|
|
1149
|
+
|
|
1150
|
+
**For Development (compose + dev override):**
|
|
1151
|
+
```yaml
|
|
1152
|
+
volumes:
|
|
1153
|
+
- ./apps/web/src:/app/apps/web/src # ✅ Hot reload
|
|
1154
|
+
- ./apps/web/public:/app/apps/web/public
|
|
1155
|
+
- ./packages/shared:/app/packages/shared
|
|
1156
|
+
```
|
|
1157
|
+
|
|
1158
|
+
**For Production (base compose only):**
|
|
1159
|
+
```yaml
|
|
1160
|
+
volumes:
|
|
1161
|
+
# No application volumes mounted – everything runs from the image
|
|
1162
|
+
```
|
|
1163
|
+
|
|
1164
|
+
⚠️ **Critical:** Avoid mounting `node_modules` or `.next` in either mode. The base image now carries the full dependency tree; mounting volumes would mask the image contents and lead to stale or empty directories.
|
|
1165
|
+
|
|
1166
|
+
## Environment Variables
|
|
1167
|
+
|
|
1168
|
+
### Complete Reference
|
|
1169
|
+
|
|
1170
|
+
All variables with their REQUIRED/OPTIONAL status:
|
|
1171
|
+
|
|
1172
|
+
```bash
|
|
1173
|
+
# =============================================================================
|
|
1174
|
+
# API CONFIGURATION (REQUIRED)
|
|
1175
|
+
# =============================================================================
|
|
1176
|
+
API_PORT=3400 # REQUIRED: Port for API server
|
|
1177
|
+
API_URL=http://localhost:3400/ # REQUIRED: External API URL
|
|
1178
|
+
API_NODE_OPTIONS=--max-old-space-size=6144 # OPTIONAL: Extra Node.js flags for the API container
|
|
1179
|
+
|
|
1180
|
+
# =============================================================================
|
|
1181
|
+
# WEB CONFIGURATION (REQUIRED)
|
|
1182
|
+
# =============================================================================
|
|
1183
|
+
PORT=3401 # REQUIRED: Port for web server
|
|
1184
|
+
APP_URL=http://localhost:3401/ # REQUIRED: External web URL
|
|
1185
|
+
NEXT_PUBLIC_API_URL=http://localhost:3400/ # REQUIRED: API URL for browser
|
|
1186
|
+
NEXT_PUBLIC_ADDRESS=http://localhost:3401 # REQUIRED: Web address for browser
|
|
1187
|
+
WEB_NODE_OPTIONS=--max-old-space-size=4096 # OPTIONAL: Extra Node.js flags for the web container
|
|
1188
|
+
|
|
1189
|
+
# =============================================================================
|
|
1190
|
+
# WORKER CONFIGURATION (OPTIONAL)
|
|
1191
|
+
# =============================================================================
|
|
1192
|
+
WORKER_NODE_OPTIONS=--max-old-space-size=6144 # OPTIONAL: Extra Node.js flags for worker containers
|
|
1193
|
+
|
|
1194
|
+
# =============================================================================
|
|
1195
|
+
# DOCKER INTERNAL NETWORKING (CRITICAL FOR MULTI-SERVER)
|
|
1196
|
+
# =============================================================================
|
|
1197
|
+
# SINGLE SERVER: Use Docker service name (default)
|
|
1198
|
+
API_INTERNAL_URL=http://api:3400/ # Works when all services on same docker-compose
|
|
1199
|
+
|
|
1200
|
+
# MULTI-SERVER: MUST use external URL or IP
|
|
1201
|
+
# API_INTERNAL_URL=https://api.example.com/ # Use actual domain
|
|
1202
|
+
# API_INTERNAL_URL=http://10.0.1.50:3400/ # Or internal IP
|
|
1203
|
+
#
|
|
1204
|
+
# ⚠️ CRITICAL: If Web and API are on different servers, you MUST set this
|
|
1205
|
+
# to the actual API server URL, NOT the Docker service name!
|
|
1206
|
+
|
|
1207
|
+
# =============================================================================
|
|
1208
|
+
# DATABASE (REQUIRED)
|
|
1209
|
+
# =============================================================================
|
|
1210
|
+
NEO4J_URI=your-neo4j-url # REQUIRED: Neo4j connection URI
|
|
1211
|
+
NEO4J_USER=neo4j # REQUIRED: Neo4j username
|
|
1212
|
+
NEO4J_PASSWORD=your-password # REQUIRED: Neo4j password
|
|
1213
|
+
NEO4J_DATABASE=your-databse # REQUIRED: Neo4j database name
|
|
1214
|
+
|
|
1215
|
+
# =============================================================================
|
|
1216
|
+
# CACHE & QUEUE (REQUIRED)
|
|
1217
|
+
# =============================================================================
|
|
1218
|
+
REDIS_HOST=localhost # REQUIRED: Redis host
|
|
1219
|
+
REDIS_PORT=6379 # REQUIRED: Redis port
|
|
1220
|
+
REDIS_PASSWORD=your-password # REQUIRED: Redis password
|
|
1221
|
+
REDIS_USERNAME=default # OPTIONAL: Redis username (default: "default")
|
|
1222
|
+
REDIS_QUEUE=your-queue # REQUIRED: Queue name for BullMQ
|
|
1223
|
+
|
|
1224
|
+
# =============================================================================
|
|
1225
|
+
# OBJECT STORAGE (REQUIRED)
|
|
1226
|
+
# =============================================================================
|
|
1227
|
+
S3_TYPE=your-type # REQUIRED: minio/s3/digitaloean/hetzner
|
|
1228
|
+
S3_ENDPOINT=your-endpoint # REQUIRED: S3/MinIO endpoint
|
|
1229
|
+
S3_BUCKET=your-bucket-name # REQUIRED: Bucket name
|
|
1230
|
+
S3_ACCESS_KEY_ID=your-access-key # REQUIRED: Access key
|
|
1231
|
+
S3_SECRET_ACCESS_KEY=your-secret # REQUIRED: Secret key
|
|
1232
|
+
S3_REGION=your-region # REQUIRED: Region
|
|
1233
|
+
|
|
1234
|
+
# =============================================================================
|
|
1235
|
+
# AUTHENTICATION (REQUIRED)
|
|
1236
|
+
# =============================================================================
|
|
1237
|
+
JWT_SECRET=your-jwt-secret # REQUIRED: JWT secret (min 32 characters)
|
|
1238
|
+
JWT_EXPIRES_IN=7d # OPTIONAL: JWT expiration (default: 7d)
|
|
1239
|
+
|
|
1240
|
+
# =============================================================================
|
|
1241
|
+
# WEB PUSH - VAPID (REQUIRED)
|
|
1242
|
+
# =============================================================================
|
|
1243
|
+
VAPID_PUBLIC_KEY=your-public-key # REQUIRED: VAPID public key
|
|
1244
|
+
VAPID_PRIVATE_KEY=your-private-key # REQUIRED: VAPID private key
|
|
1245
|
+
VAPID_EMAIL=your-email@example.com # OPTIONAL: Contact email for VAPID
|
|
1246
|
+
NEXT_PUBLIC_VAPID_PUBLIC_KEY=your-public-key # REQUIRED: Public key for browser
|
|
1247
|
+
|
|
1248
|
+
# =============================================================================
|
|
1249
|
+
# CORS CONFIGURATION (OPTIONAL)
|
|
1250
|
+
# =============================================================================
|
|
1251
|
+
CORS_ORIGINS=http://localhost:3401 # OPTIONAL: Allowed CORS origins
|
|
1252
|
+
|
|
1253
|
+
# =============================================================================
|
|
1254
|
+
# EMAIL CONFIGURATION (OPTIONAL)
|
|
1255
|
+
# =============================================================================
|
|
1256
|
+
EMAIL_PROVIDER=smtp # OPTIONAL: Email provider
|
|
1257
|
+
EMAIL_API_KEY=your-api-key # OPTIONAL: API key for email provider
|
|
1258
|
+
EMAIL_FROM=noreply@example.com # OPTIONAL: From email address
|
|
1259
|
+
EMAIL_HOST=smtp.example.com # OPTIONAL: SMTP host
|
|
1260
|
+
EMAIL_PORT=587 # OPTIONAL: SMTP port
|
|
1261
|
+
EMAIL_SECURE=false # OPTIONAL: Use TLS/SSL
|
|
1262
|
+
EMAIL_USERNAME=your-username # OPTIONAL: SMTP username
|
|
1263
|
+
EMAIL_PASSWORD=your-password # OPTIONAL: SMTP password
|
|
1264
|
+
|
|
1265
|
+
# =============================================================================
|
|
1266
|
+
# CACHING (OPTIONAL)
|
|
1267
|
+
# =============================================================================
|
|
1268
|
+
CACHE_ENABLED=true # OPTIONAL: Enable/disable caching (default: true)
|
|
1269
|
+
CACHE_DEFAULT_TTL=3600 # OPTIONAL: Default cache TTL in seconds
|
|
1270
|
+
|
|
1271
|
+
# =============================================================================
|
|
1272
|
+
# RATE LIMITING (OPTIONAL)
|
|
1273
|
+
# =============================================================================
|
|
1274
|
+
RATE_LIMIT_ENABLED=true # OPTIONAL: Enable rate limiting (default: true)
|
|
1275
|
+
RATE_LIMIT_TTL=60 # OPTIONAL: Rate limit window in seconds
|
|
1276
|
+
RATE_LIMIT_REQUESTS=100 # OPTIONAL: Max requests per window
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
### Environment Variable Hierarchy
|
|
1280
|
+
|
|
1281
|
+
Docker Compose resolves variables in this order:
|
|
1282
|
+
1. Environment variables set in shell
|
|
1283
|
+
2. `.env` file in project root
|
|
1284
|
+
3. Default values in docker-compose.yml (e.g., `${API_PORT:-3400}`)
|
|
1285
|
+
|
|
1286
|
+
### Verifying Environment Variables
|
|
1287
|
+
|
|
1288
|
+
```bash
|
|
1289
|
+
# Check resolved docker-compose configuration
|
|
1290
|
+
docker compose config
|
|
1291
|
+
|
|
1292
|
+
# Check variables inside running container
|
|
1293
|
+
docker compose exec web printenv | grep API
|
|
1294
|
+
docker compose exec api printenv | grep NEO4J
|
|
1295
|
+
```
|
|
1296
|
+
|
|
1297
|
+
## Troubleshooting
|
|
1298
|
+
|
|
1299
|
+
### Issue: 500 Error on Page Load (Timeout)
|
|
1300
|
+
|
|
1301
|
+
**Symptoms:**
|
|
1302
|
+
- Page loads after 30+ second timeout
|
|
1303
|
+
- Browser shows: `Error: 500:`
|
|
1304
|
+
- No logs in API container
|
|
1305
|
+
|
|
1306
|
+
**Cause:** Web container can't reach API via Docker network
|
|
1307
|
+
|
|
1308
|
+
**Solution:**
|
|
1309
|
+
1. Verify `API_INTERNAL_URL` environment variable:
|
|
1310
|
+
```bash
|
|
1311
|
+
docker compose exec web printenv | grep API_INTERNAL_URL
|
|
1312
|
+
# Should show: API_INTERNAL_URL=http://api:3400/
|
|
1313
|
+
```
|
|
1314
|
+
|
|
1315
|
+
2. Test connectivity:
|
|
1316
|
+
```bash
|
|
1317
|
+
docker compose exec web wget -O- http://api:3400
|
|
1318
|
+
# Should return API response, not timeout
|
|
1319
|
+
```
|
|
1320
|
+
|
|
1321
|
+
3. Check both services are on same network:
|
|
1322
|
+
```bash
|
|
1323
|
+
docker compose ps
|
|
1324
|
+
# Both should show "{{name}}-network"
|
|
1325
|
+
```
|
|
1326
|
+
|
|
1327
|
+
4. If still failing, restart with network reset:
|
|
1328
|
+
```bash
|
|
1329
|
+
docker compose down
|
|
1330
|
+
docker compose up
|
|
1331
|
+
```
|
|
1332
|
+
|
|
1333
|
+
### Issue: Production Build Not Found
|
|
1334
|
+
|
|
1335
|
+
**Symptoms:**
|
|
1336
|
+
```
|
|
1337
|
+
Could not find a production build in the '.next' directory
|
|
1338
|
+
```
|
|
1339
|
+
|
|
1340
|
+
**Cause:** The `.next` volume mount is overwriting the built files
|
|
1341
|
+
|
|
1342
|
+
**Solution:**
|
|
1343
|
+
Check `docker-compose.yml` and ensure this line is commented out:
|
|
1344
|
+
```yaml
|
|
1345
|
+
# - web-next:/app/apps/web/.next # MUST be commented in production!
|
|
1346
|
+
```
|
|
1347
|
+
|
|
1348
|
+
Then rebuild:
|
|
1349
|
+
```bash
|
|
1350
|
+
docker compose down
|
|
1351
|
+
docker compose build
|
|
1352
|
+
docker compose up
|
|
1353
|
+
```
|
|
1354
|
+
|
|
1355
|
+
### Issue: Code Changes Not Reflected
|
|
1356
|
+
|
|
1357
|
+
**Symptoms:** You edit code but changes don't appear in the running container
|
|
1358
|
+
|
|
1359
|
+
**Cause:** Running production build or volumes not mounted
|
|
1360
|
+
|
|
1361
|
+
**Solution:**
|
|
1362
|
+
1. Make sure you are using the dev override:
|
|
1363
|
+
```bash
|
|
1364
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml ps
|
|
1365
|
+
```
|
|
1366
|
+
If this command fails, you likely started only the base compose (production mode).
|
|
1367
|
+
|
|
1368
|
+
2. Rebuild the dev images:
|
|
1369
|
+
```bash
|
|
1370
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml down
|
|
1371
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml build --no-cache
|
|
1372
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
|
|
1373
|
+
```
|
|
1374
|
+
|
|
1375
|
+
3. Check volumes are mounted:
|
|
1376
|
+
```bash
|
|
1377
|
+
docker compose config | grep -A 5 volumes
|
|
1378
|
+
```
|
|
1379
|
+
|
|
1380
|
+
### Issue: Build Fails with "Cannot find module"
|
|
1381
|
+
|
|
1382
|
+
**Cause:** Dependencies not installed or cached incorrectly
|
|
1383
|
+
|
|
1384
|
+
**Solution:**
|
|
1385
|
+
```bash
|
|
1386
|
+
# Clear build cache and rebuild
|
|
1387
|
+
docker compose build --no-cache
|
|
1388
|
+
|
|
1389
|
+
# If still failing, remove volumes
|
|
1390
|
+
docker compose down -v
|
|
1391
|
+
docker compose build
|
|
1392
|
+
docker compose up
|
|
1393
|
+
```
|
|
1394
|
+
|
|
1395
|
+
### Issue: API Container Exits Immediately
|
|
1396
|
+
|
|
1397
|
+
**Symptoms:** `docker compose ps` shows API as "Exited (1)"
|
|
1398
|
+
|
|
1399
|
+
**Solution:**
|
|
1400
|
+
```bash
|
|
1401
|
+
# Check logs for error
|
|
1402
|
+
docker compose logs api
|
|
1403
|
+
|
|
1404
|
+
# Common issues:
|
|
1405
|
+
# 1. Can't connect to Neo4j/Redis - verify connection strings
|
|
1406
|
+
# 2. Missing environment variables - check .env file
|
|
1407
|
+
# 3. Build error - rebuild with --no-cache
|
|
1408
|
+
```
|
|
1409
|
+
|
|
1410
|
+
### Issue: Port Already in Use
|
|
1411
|
+
|
|
1412
|
+
**Symptoms:**
|
|
1413
|
+
```
|
|
1414
|
+
Error starting userland proxy: listen tcp 0.0.0.0:3400: bind: address already in use
|
|
1415
|
+
```
|
|
1416
|
+
|
|
1417
|
+
**Solution:**
|
|
1418
|
+
```bash
|
|
1419
|
+
# Find process using port (macOS/Linux)
|
|
1420
|
+
lsof -i :3400
|
|
1421
|
+
|
|
1422
|
+
# Find process using port (Windows)
|
|
1423
|
+
netstat -ano | findstr :3400
|
|
1424
|
+
|
|
1425
|
+
# Either kill the process or change port in .env:
|
|
1426
|
+
PORT=3402 # for web
|
|
1427
|
+
API_PORT=3402 # for api
|
|
1428
|
+
```
|
|
1429
|
+
|
|
1430
|
+
### Issue: Out of Disk Space
|
|
1431
|
+
|
|
1432
|
+
**Solution:**
|
|
1433
|
+
```bash
|
|
1434
|
+
# Check Docker disk usage
|
|
1435
|
+
docker system df
|
|
1436
|
+
|
|
1437
|
+
# Remove unused images and containers
|
|
1438
|
+
docker system prune -a
|
|
1439
|
+
|
|
1440
|
+
# Remove volumes (WARNING: deletes data)
|
|
1441
|
+
docker system prune -a --volumes
|
|
1442
|
+
```
|
|
1443
|
+
|
|
1444
|
+
### Issue: NEXT_PUBLIC_* Variable Changes Not Applied
|
|
1445
|
+
|
|
1446
|
+
**Cause:** Next.js bakes `NEXT_PUBLIC_*` variables into the build at build time
|
|
1447
|
+
|
|
1448
|
+
**Solution:**
|
|
1449
|
+
After changing any `NEXT_PUBLIC_*` variable:
|
|
1450
|
+
```bash
|
|
1451
|
+
# Must rebuild web container
|
|
1452
|
+
docker compose build --no-cache web
|
|
1453
|
+
docker compose up web
|
|
1454
|
+
```
|
|
1455
|
+
|
|
1456
|
+
## Advanced Topics
|
|
1457
|
+
|
|
1458
|
+
### Running Individual Services
|
|
1459
|
+
|
|
1460
|
+
```bash
|
|
1461
|
+
# Start only API (and its dependencies)
|
|
1462
|
+
docker compose up api
|
|
1463
|
+
|
|
1464
|
+
# Start only Web (and its dependencies)
|
|
1465
|
+
docker compose up web
|
|
1466
|
+
|
|
1467
|
+
# Start only Worker
|
|
1468
|
+
docker compose up worker
|
|
1469
|
+
```
|
|
1470
|
+
|
|
1471
|
+
### Viewing Logs
|
|
1472
|
+
|
|
1473
|
+
```bash
|
|
1474
|
+
# Follow all logs
|
|
1475
|
+
docker compose logs -f
|
|
1476
|
+
|
|
1477
|
+
# Follow specific service
|
|
1478
|
+
docker compose logs -f web
|
|
1479
|
+
|
|
1480
|
+
# Show last 100 lines
|
|
1481
|
+
docker compose logs --tail=100 api
|
|
1482
|
+
|
|
1483
|
+
# Filter logs
|
|
1484
|
+
docker compose logs web | grep Error
|
|
1485
|
+
```
|
|
1486
|
+
|
|
1487
|
+
### Executing Commands in Containers
|
|
1488
|
+
|
|
1489
|
+
```bash
|
|
1490
|
+
# Open shell in container
|
|
1491
|
+
docker compose exec web sh
|
|
1492
|
+
docker compose exec api sh
|
|
1493
|
+
|
|
1494
|
+
# Run single command
|
|
1495
|
+
docker compose exec api npm run migrate
|
|
1496
|
+
docker compose exec web ls -la /app/apps/web/.next
|
|
1497
|
+
|
|
1498
|
+
# Check environment
|
|
1499
|
+
docker compose exec web printenv
|
|
1500
|
+
```
|
|
1501
|
+
|
|
1502
|
+
### Scaling Workers
|
|
1503
|
+
|
|
1504
|
+
```bash
|
|
1505
|
+
# Run 3 worker instances
|
|
1506
|
+
docker compose up --scale worker=3
|
|
1507
|
+
```
|
|
1508
|
+
|
|
1509
|
+
### Cleaning Up
|
|
1510
|
+
|
|
1511
|
+
```bash
|
|
1512
|
+
# Stop services
|
|
1513
|
+
docker compose down
|
|
1514
|
+
|
|
1515
|
+
# Stop and remove volumes
|
|
1516
|
+
docker compose down -v
|
|
1517
|
+
|
|
1518
|
+
# Remove all {{name}} containers and images
|
|
1519
|
+
docker compose down --rmi all --volumes
|
|
1520
|
+
|
|
1521
|
+
# Clean up Docker system
|
|
1522
|
+
docker system prune -a
|
|
1523
|
+
```
|
|
1524
|
+
|
|
1525
|
+
### Resource Monitoring
|
|
1526
|
+
|
|
1527
|
+
```bash
|
|
1528
|
+
# Live resource usage
|
|
1529
|
+
docker stats
|
|
1530
|
+
|
|
1531
|
+
# Container processes
|
|
1532
|
+
docker compose top
|
|
1533
|
+
|
|
1534
|
+
# Disk usage
|
|
1535
|
+
docker system df
|
|
1536
|
+
```
|
|
1537
|
+
|
|
1538
|
+
## Switching Between Dev and Production
|
|
1539
|
+
|
|
1540
|
+
The base compose file is production-oriented; add the dev override when you need hot reload.
|
|
1541
|
+
|
|
1542
|
+
```bash
|
|
1543
|
+
# Production (default)
|
|
1544
|
+
docker compose down
|
|
1545
|
+
docker compose up --build -d
|
|
1546
|
+
|
|
1547
|
+
# Development (hot reload)
|
|
1548
|
+
docker compose down
|
|
1549
|
+
docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build
|
|
1550
|
+
```
|
|
1551
|
+
|
|
1552
|
+
**Note:** Always include `--build` when switching modes so Docker rebuilds the correct stage.
|
|
1553
|
+
|
|
1554
|
+
## Best Practices
|
|
1555
|
+
|
|
1556
|
+
### Development
|
|
1557
|
+
|
|
1558
|
+
1. **Use the dev override** (`-f docker-compose.yml -f docker-compose.dev.yml`) for active development
|
|
1559
|
+
2. **Keep source/shared volumes mounted** for hot-reload (default configuration)
|
|
1560
|
+
3. **Don't commit `.env`** - use `.env.example` as template
|
|
1561
|
+
4. **Check logs regularly** when something doesn't work
|
|
1562
|
+
5. **Rebuild after dependency changes** (package.json modifications)
|
|
1563
|
+
|
|
1564
|
+
### Production
|
|
1565
|
+
|
|
1566
|
+
1. **Run the base compose file only** for production deployments
|
|
1567
|
+
2. **Never commit secrets** - use environment variable management tools
|
|
1568
|
+
3. **Avoid mounting `node_modules` or `.next`** to prevent dependency drift
|
|
1569
|
+
4. **Use specific image tags** instead of `latest`
|
|
1570
|
+
5. **Enable health checks** and monitoring
|
|
1571
|
+
6. **Rebuild after ANY environment variable change** affecting build
|
|
1572
|
+
|
|
1573
|
+
### Both Modes
|
|
1574
|
+
|
|
1575
|
+
1. **Run external infrastructure** (Neo4j, Redis, MinIO) before starting services
|
|
1576
|
+
2. **Verify network connectivity** if services can't communicate
|
|
1577
|
+
3. **Monitor disk space** - Docker images can consume significant space
|
|
1578
|
+
4. **Use `.dockerignore`** to exclude unnecessary files from build context
|
|
1579
|
+
5. **Clean up regularly** with `docker system prune`
|
|
1580
|
+
|
|
1581
|
+
## Additional Resources
|
|
1582
|
+
|
|
1583
|
+
- [Docker Compose Documentation](https://docs.docker.com/compose/)
|
|
1584
|
+
- [Next.js Docker Documentation](https://nextjs.org/docs/deployment#docker-image)
|
|
1585
|
+
- [NestJS Docker Documentation](https://docs.nestjs.com/recipes/docker)
|
|
1586
|
+
- [Multi-stage Docker Builds](https://docs.docker.com/build/building/multi-stage/)
|
|
1587
|
+
|
|
1588
|
+
---
|
|
1589
|
+
|
|
1590
|
+
**Questions or Issues?**
|
|
1591
|
+
Check the main [README.md](./README.md) or open an issue on the repository.
|