739 lines
21 KiB
Markdown
739 lines
21 KiB
Markdown
# Docker Production Build Guide
|
|
|
|
## Goal
|
|
Build a production-ready Docker image containing both the frontend (React/Vite) and backend (Express/Node.js) that connects to an external MySQL database. The container will serve the frontend on port 5173 and proxy API requests to the backend running on localhost:3000 inside the container.
|
|
|
|
**SUCCESS CRITERIA**: Docker image is built successfully and ready for deployment.
|
|
|
|
## Architecture
|
|
- **Single Docker Container**: Contains both FE and BE
|
|
- **External Database**: MySQL server (not in container)
|
|
- **Port Exposure**: Only port 5173 (frontend) exposed
|
|
- **Internal Communication**: FE → BE via localhost:3000 inside container
|
|
- **Reverse Proxy**: Caddy handles SSL/domain externally (Unraid setup)
|
|
|
|
---
|
|
|
|
## Task 1: Create Dockerfile
|
|
|
|
**File**: `Dockerfile` (in project root)
|
|
|
|
### Requirements:
|
|
1. **Multi-stage build** for optimal image size
|
|
2. **Stage 1 - Build Frontend**:
|
|
- Use `node:20-alpine` as base
|
|
- Set working directory to `/app`
|
|
- Copy `package.json`, `pnpm-lock.yaml`, `pnpm-workspace.yaml`
|
|
- Copy `packages/frontend/package.json`
|
|
- Install pnpm globally: `npm install -g pnpm`
|
|
- Install dependencies: `pnpm install --frozen-lockfile`
|
|
- Copy entire `packages/frontend` directory
|
|
- Build frontend: `cd packages/frontend && pnpm build`
|
|
- Output will be in `packages/frontend/dist`
|
|
|
|
3. **Stage 2 - Build Backend**:
|
|
- Use `node:20-alpine` as base
|
|
- Set working directory to `/app`
|
|
- Copy workspace files (same as stage 1)
|
|
- Install pnpm globally
|
|
- Install dependencies: `pnpm install --frozen-lockfile`
|
|
- Copy entire `packages/backend` directory
|
|
- Generate Prisma client: `cd packages/backend && npx prisma generate`
|
|
- Build backend: `cd packages/backend && pnpm build`
|
|
- Output will be in `packages/backend/dist`
|
|
|
|
4. **Stage 3 - Production Runtime**:
|
|
- Use `node:20-alpine` as base
|
|
- Install pnpm globally and `serve` package globally: `npm install -g pnpm serve`
|
|
- Set working directory to `/app`
|
|
- Copy production dependencies setup:
|
|
- Copy `package.json`, `pnpm-workspace.yaml`, `pnpm-lock.yaml`
|
|
- Copy `packages/backend/package.json` to `packages/backend/`
|
|
- Copy `packages/frontend/package.json` to `packages/frontend/`
|
|
- Install ONLY production dependencies: `pnpm install --prod --frozen-lockfile`
|
|
- Copy backend build artifacts:
|
|
- From stage 2: `packages/backend/dist` → `/app/packages/backend/dist`
|
|
- From stage 2: `packages/backend/prisma` → `/app/packages/backend/prisma`
|
|
- From stage 2: `packages/backend/node_modules/.prisma` → `/app/packages/backend/node_modules/.prisma`
|
|
- Copy frontend build artifacts:
|
|
- From stage 1: `packages/frontend/dist` → `/app/packages/frontend/dist`
|
|
- Create uploads directory: `mkdir -p /app/packages/backend/uploads`
|
|
- Expose port 5173
|
|
- Create startup script (see Task 2)
|
|
- Set CMD to run startup script
|
|
|
|
### Important Notes:
|
|
- Use `.dockerignore` to exclude `node_modules`, `dist`, `.env` files
|
|
- Ensure Prisma client is generated and copied correctly
|
|
- Backend needs access to Prisma schema for migrations
|
|
|
|
---
|
|
|
|
## Task 2: Create Startup Script
|
|
|
|
**File**: `docker-entrypoint.sh` (in project root)
|
|
|
|
### Requirements:
|
|
1. **Bash script** with proper shebang: `#!/bin/sh`
|
|
2. **Set error handling**: `set -e`
|
|
3. **Environment validation**:
|
|
- Check required env vars: `DATABASE_URL`, `SESSION_SECRET`
|
|
- Exit with error message if missing
|
|
4. **Database migration**:
|
|
- Navigate to backend: `cd /app/packages/backend`
|
|
- Run Prisma migrations: `npx prisma migrate deploy`
|
|
- Optional: Run seed if `RUN_SEED=true` env var is set
|
|
5. **Start backend**:
|
|
- Start backend in background: `node dist/server.js &`
|
|
- Store PID: `BACKEND_PID=$!`
|
|
6. **Wait for backend**:
|
|
- Add 5-second sleep or health check loop
|
|
- Ensure backend is ready before starting frontend
|
|
7. **Start frontend**:
|
|
- Serve frontend on port 5173: `serve -s /app/packages/frontend/dist -l 5173`
|
|
8. **Signal handling**:
|
|
- Trap SIGTERM/SIGINT to gracefully shutdown both processes
|
|
- Kill backend PID on exit
|
|
|
|
### Script Template:
|
|
```bash
|
|
#!/bin/sh
|
|
set -e
|
|
|
|
echo "🚀 Starting Znakovni.hr Production Container..."
|
|
|
|
# Validate environment
|
|
if [ -z "$DATABASE_URL" ]; then
|
|
echo "❌ ERROR: DATABASE_URL is required"
|
|
exit 1
|
|
fi
|
|
|
|
# Run migrations
|
|
cd /app/packages/backend
|
|
echo "📦 Running database migrations..."
|
|
npx prisma migrate deploy
|
|
|
|
# Start backend
|
|
echo "🔧 Starting backend server..."
|
|
cd /app/packages/backend
|
|
node dist/server.js &
|
|
BACKEND_PID=$!
|
|
|
|
# Wait for backend
|
|
sleep 5
|
|
|
|
# Start frontend
|
|
echo "🎨 Starting frontend server..."
|
|
cd /app
|
|
serve -s packages/frontend/dist -l 5173 -n &
|
|
FRONTEND_PID=$!
|
|
|
|
# Trap signals
|
|
trap "kill $BACKEND_PID $FRONTEND_PID" SIGTERM SIGINT
|
|
|
|
# Wait for processes
|
|
wait $BACKEND_PID $FRONTEND_PID
|
|
```
|
|
|
|
---
|
|
|
|
## Task 3: Create .dockerignore
|
|
|
|
**File**: `.dockerignore` (in project root)
|
|
|
|
### Exclude:
|
|
```
|
|
node_modules
|
|
dist
|
|
.env
|
|
.env.local
|
|
.env.*.local
|
|
*.log
|
|
.git
|
|
.gitignore
|
|
.vscode
|
|
.idea
|
|
*.md
|
|
!README.md
|
|
packages/*/node_modules
|
|
packages/*/dist
|
|
packages/backend/uploads/*
|
|
!packages/backend/uploads/.gitkeep
|
|
.DS_Store
|
|
```
|
|
|
|
---
|
|
|
|
## Task 4: Update Frontend Build Configuration
|
|
|
|
**File**: `packages/frontend/vite.config.ts`
|
|
|
|
### Changes Required:
|
|
1. **Remove hardcoded host**: Change `host: '192.168.1.238'` to `host: '0.0.0.0'` or remove entirely
|
|
2. **Production build**: Ensure build outputs to `dist` directory
|
|
3. **API proxy**: Remove proxy config (not needed in production, handled by env var)
|
|
|
|
### Updated Config:
|
|
```typescript
|
|
import { defineConfig } from 'vite';
|
|
import react from '@vitejs/plugin-react';
|
|
import path from 'path';
|
|
|
|
export default defineConfig({
|
|
plugins: [react()],
|
|
resolve: {
|
|
alias: {
|
|
'@': path.resolve(__dirname, './src'),
|
|
},
|
|
},
|
|
server: {
|
|
host: '0.0.0.0',
|
|
port: 5173,
|
|
proxy: {
|
|
'/api': {
|
|
target: 'http://localhost:3000',
|
|
changeOrigin: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Task 5: Update Frontend API Configuration
|
|
|
|
**File**: `packages/frontend/.env.production` (create new file)
|
|
|
|
### Content:
|
|
```env
|
|
VITE_API_URL=http://localhost:3000
|
|
```
|
|
|
|
**Note**: In production Docker container, frontend connects to backend via localhost since they're in the same container.
|
|
|
|
---
|
|
|
|
## Task 6: Update Backend Server Configuration
|
|
|
|
**File**: `packages/backend/src/server.ts`
|
|
|
|
### Verify/Update:
|
|
1. **HOST binding**: Ensure `HOST = process.env.HOST || '0.0.0.0'` (line 85)
|
|
2. **PORT**: Ensure `PORT = parseInt(process.env.PORT || '3000', 10)` (line 16)
|
|
3. **CORS origin**: Should accept `FRONTEND_URL` from env (line 24)
|
|
4. **Static uploads**: Ensure uploads path is correct (line 57-58)
|
|
|
|
### No changes needed if these are already correct (they are based on codebase retrieval).
|
|
|
|
---
|
|
|
|
## Task 7: Create Docker Compose Example
|
|
|
|
**File**: `docker-compose.yml` (in project root)
|
|
|
|
### Purpose:
|
|
Provide example for running the container with proper environment variables.
|
|
|
|
### Content:
|
|
```yaml
|
|
version: '3.8'
|
|
|
|
services:
|
|
znakovni:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
image: znakovni:latest
|
|
container_name: znakovni-app
|
|
restart: unless-stopped
|
|
ports:
|
|
- "5173:5173"
|
|
environment:
|
|
# Database Configuration
|
|
DATABASE_URL: "mysql://user:password@192.168.1.74:3306/znakovni"
|
|
# Optional: Shadow database for Prisma migrations
|
|
# SHADOW_DATABASE_URL: "mysql://user:password@192.168.1.74:3306/znakovni_shadow"
|
|
|
|
# Server Configuration
|
|
NODE_ENV: production
|
|
PORT: 3000
|
|
HOST: 0.0.0.0
|
|
FRONTEND_URL: http://localhost:5173
|
|
|
|
# Session Secret (CHANGE THIS!)
|
|
SESSION_SECRET: your-super-secret-session-key-change-in-production-min-32-chars
|
|
|
|
# OAuth - Google (optional, configure if using)
|
|
# GOOGLE_CLIENT_ID: your-google-client-id
|
|
# GOOGLE_CLIENT_SECRET: your-google-client-secret
|
|
# GOOGLE_CALLBACK_URL: https://yourdomain.com/api/auth/google/callback
|
|
|
|
# OAuth - Microsoft (optional, configure if using)
|
|
# MICROSOFT_CLIENT_ID: your-microsoft-client-id
|
|
# MICROSOFT_CLIENT_SECRET: your-microsoft-client-secret
|
|
# MICROSOFT_CALLBACK_URL: https://yourdomain.com/api/auth/microsoft/callback
|
|
|
|
# File Upload
|
|
UPLOAD_DIR: ./uploads
|
|
MAX_FILE_SIZE: 104857600
|
|
|
|
# Optional: Run database seed on startup
|
|
# RUN_SEED: "false"
|
|
|
|
volumes:
|
|
# Persist uploaded files
|
|
- ./uploads:/app/packages/backend/uploads
|
|
|
|
networks:
|
|
- znakovni-network
|
|
|
|
# Health check
|
|
healthcheck:
|
|
test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:3000/api/health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 40s
|
|
|
|
networks:
|
|
znakovni-network:
|
|
driver: bridge
|
|
```
|
|
|
|
---
|
|
|
|
## Task 8: Create Production Environment Template
|
|
|
|
**File**: `.env.production.example` (in project root)
|
|
|
|
### Content:
|
|
```env
|
|
# ===========================================
|
|
# ZNAKOVNI.HR PRODUCTION CONFIGURATION
|
|
# ===========================================
|
|
|
|
# Database Configuration (REQUIRED)
|
|
# Point to your external MySQL server
|
|
DATABASE_URL="mysql://username:password@192.168.1.74:3306/znakovni"
|
|
|
|
# Optional: Shadow database for Prisma migrations
|
|
# SHADOW_DATABASE_URL="mysql://username:password@192.168.1.74:3306/znakovni_shadow"
|
|
|
|
# Server Configuration
|
|
NODE_ENV=production
|
|
PORT=3000
|
|
HOST=0.0.0.0
|
|
FRONTEND_URL=http://localhost:5173
|
|
|
|
# Session Secret (REQUIRED - CHANGE THIS!)
|
|
# Generate with: openssl rand -base64 32
|
|
SESSION_SECRET=CHANGE-THIS-TO-A-RANDOM-STRING-MIN-32-CHARACTERS-LONG
|
|
|
|
# OAuth - Google (Optional)
|
|
GOOGLE_CLIENT_ID=
|
|
GOOGLE_CLIENT_SECRET=
|
|
GOOGLE_CALLBACK_URL=https://yourdomain.com/api/auth/google/callback
|
|
|
|
# OAuth - Microsoft (Optional)
|
|
MICROSOFT_CLIENT_ID=
|
|
MICROSOFT_CLIENT_SECRET=
|
|
MICROSOFT_CALLBACK_URL=https://yourdomain.com/api/auth/microsoft/callback
|
|
|
|
# File Upload Configuration
|
|
UPLOAD_DIR=./uploads
|
|
MAX_FILE_SIZE=104857600
|
|
|
|
# Database Seeding (Optional)
|
|
# Set to "true" to run seed on container startup
|
|
RUN_SEED=false
|
|
```
|
|
|
|
---
|
|
|
|
## Task 9: Build Docker Image
|
|
|
|
### Build Command:
|
|
```bash
|
|
# From project root
|
|
docker build -t znakovni:latest .
|
|
```
|
|
|
|
### Verify Build Success:
|
|
```bash
|
|
# Check image exists
|
|
docker images | grep znakovni
|
|
|
|
# Should show:
|
|
# znakovni latest <image-id> <time> <size>
|
|
```
|
|
|
|
### Expected Build Output:
|
|
- ✅ Stage 1: Frontend build completes successfully
|
|
- ✅ Stage 2: Backend build completes successfully
|
|
- ✅ Stage 3: Production image created
|
|
- ✅ Image tagged as `znakovni:latest`
|
|
- ✅ No build errors
|
|
|
|
**TASK COMPLETE** when image is built and shows in `docker images` output.
|
|
|
|
---
|
|
|
|
## Task 10: Optional - Test Container Locally
|
|
|
|
**NOTE**: This task is OPTIONAL. The main goal is to build the image. User will handle deployment.
|
|
|
|
### Run Container (Manual):
|
|
```bash
|
|
docker run -d \
|
|
--name znakovni-app \
|
|
-p 5173:5173 \
|
|
-e DATABASE_URL="mysql://user:password@192.168.1.74:3306/znakovni" \
|
|
-e SESSION_SECRET="your-secret-key-here" \
|
|
-e NODE_ENV=production \
|
|
-v $(pwd)/uploads:/app/packages/backend/uploads \
|
|
znakovni:latest
|
|
```
|
|
|
|
### Run with Docker Compose:
|
|
```bash
|
|
# Edit docker-compose.yml with your database credentials
|
|
docker-compose up -d
|
|
|
|
# View logs
|
|
docker-compose logs -f
|
|
|
|
# Stop
|
|
docker-compose down
|
|
```
|
|
|
|
### Verify Container:
|
|
```bash
|
|
# Check if container is running
|
|
docker ps
|
|
|
|
# Check logs
|
|
docker logs znakovni-app
|
|
|
|
# Test backend health
|
|
curl http://localhost:5173/api/health
|
|
|
|
# Test frontend
|
|
curl http://localhost:5173
|
|
```
|
|
|
|
---
|
|
|
|
## Task 11: Save Docker Image for Deployment
|
|
|
|
**NOTE**: This task is for preparing the image for transfer to Unraid. User will handle actual deployment.
|
|
|
|
### Save Image to File:
|
|
```bash
|
|
# Save image to tar file
|
|
docker save znakovni:latest -o znakovni.tar
|
|
|
|
# Verify file created
|
|
ls -lh znakovni.tar
|
|
```
|
|
|
|
### Image is Ready for Deployment
|
|
The `znakovni.tar` file can now be:
|
|
- Transferred to Unraid server
|
|
- Loaded with: `docker load -i znakovni.tar`
|
|
- Deployed by user
|
|
|
|
---
|
|
|
|
## DEPLOYMENT REFERENCE (For User)
|
|
|
|
### Unraid Deployment Steps:
|
|
1. **Transfer image**: `scp znakovni.tar user@unraid:/mnt/user/appdata/`
|
|
2. **Load image**: `docker load -i znakovni.tar`
|
|
3. **Create container** in Unraid UI:
|
|
- **Repository**: `znakovni:latest`
|
|
- **Port**: `5173` → `5173` (or any host port)
|
|
- **Environment Variables**: Add all required vars from `.env.production.example`
|
|
- **Volume**: Map host path to `/app/packages/backend/uploads`
|
|
4. **Configure Caddy** reverse proxy:
|
|
- Point domain to `http://unraid-ip:5173`
|
|
- Caddy handles SSL/HTTPS
|
|
|
|
### Caddy Configuration Example:
|
|
```
|
|
znakovni.yourdomain.com {
|
|
reverse_proxy http://localhost:5173
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Task 12: Troubleshooting Build Issues
|
|
|
|
### Build-Time Issues:
|
|
|
|
1. **pnpm install fails**:
|
|
- Ensure `pnpm-lock.yaml` is present
|
|
- Check Node.js version compatibility (needs Node 20)
|
|
- Verify `pnpm-workspace.yaml` is copied correctly
|
|
|
|
2. **Frontend build fails**:
|
|
- Check TypeScript compilation errors
|
|
- Verify all dependencies are installed
|
|
- Ensure Vite config is correct
|
|
- Check for missing environment variables
|
|
|
|
3. **Backend build fails**:
|
|
- Check TypeScript compilation errors
|
|
- Verify Prisma schema is valid
|
|
- Ensure all dependencies are installed
|
|
|
|
4. **Prisma generate fails**:
|
|
- Verify `DATABASE_URL` format is correct (even for build)
|
|
- Check Prisma schema syntax
|
|
- Ensure MySQL provider is specified
|
|
|
|
5. **Docker build context too large**:
|
|
- Verify `.dockerignore` is present and correct
|
|
- Ensure `node_modules` and `dist` are excluded
|
|
|
|
### Debug Build:
|
|
```bash
|
|
# Build with no cache to see all steps
|
|
docker build --no-cache -t znakovni:latest .
|
|
|
|
# Build with progress output
|
|
docker build --progress=plain -t znakovni:latest .
|
|
|
|
# Check build context size
|
|
docker build --no-cache -t znakovni:latest . 2>&1 | grep "Sending build context"
|
|
```
|
|
|
|
### Runtime Issues (If Testing Container):
|
|
|
|
1. **Database Connection Failed**:
|
|
- Verify `DATABASE_URL` is correct
|
|
- Ensure MySQL server allows connections from Docker container IP
|
|
- Check MySQL user permissions: `GRANT ALL ON znakovni.* TO 'user'@'%';`
|
|
|
|
2. **Frontend Can't Reach Backend**:
|
|
- Verify `VITE_API_URL=http://localhost:3000` in frontend build
|
|
- Check backend is listening on `0.0.0.0:3000`
|
|
- Verify CORS settings in backend
|
|
|
|
3. **Container Exits Immediately**:
|
|
- Check logs: `docker logs znakovni-app`
|
|
- Verify all required env vars are set
|
|
- Ensure database is accessible
|
|
|
|
### Debug Commands (For Running Container):
|
|
```bash
|
|
# Enter running container
|
|
docker exec -it znakovni-app sh
|
|
|
|
# Check backend process
|
|
docker exec znakovni-app ps aux | grep node
|
|
|
|
# Check environment variables
|
|
docker exec znakovni-app env | grep DATABASE
|
|
```
|
|
|
|
---
|
|
|
|
## REFERENCE: Security Checklist (For User - Deployment Phase)
|
|
|
|
Before deploying to production:
|
|
|
|
- [ ] Change `SESSION_SECRET` to a strong random string (min 32 chars)
|
|
- [ ] Use strong MySQL password
|
|
- [ ] Configure OAuth credentials if using Google/Microsoft login
|
|
- [ ] Update OAuth callback URLs to production domain
|
|
- [ ] Ensure MySQL server has firewall rules (only allow necessary IPs)
|
|
- [ ] Set up regular database backups
|
|
- [ ] Configure Caddy with proper SSL certificates
|
|
- [ ] Review and restrict MySQL user permissions
|
|
- [ ] Set up monitoring/logging for the container
|
|
- [ ] Configure automatic container restarts (`restart: unless-stopped`)
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
This setup creates a **fully self-contained Docker image** with:
|
|
- ✅ Frontend (React/Vite) built and served on port 5173
|
|
- ✅ Backend (Express/Node.js) running on internal port 3000
|
|
- ✅ Prisma ORM with automatic migrations on startup
|
|
- ✅ External MySQL database connection
|
|
- ✅ Persistent uploads via Docker volumes
|
|
- ✅ Health checks and graceful shutdown
|
|
- ✅ Production-optimized multi-stage build
|
|
- ✅ Ready for Unraid + Caddy deployment
|
|
|
|
---
|
|
|
|
## IMPLEMENTATION PLAN FOR AI AGENTS
|
|
|
|
### Phase 1: Create Required Files (Tasks 1-8)
|
|
1. ✅ Create `Dockerfile` with multi-stage build
|
|
2. ✅ Create `docker-entrypoint.sh` startup script
|
|
3. ✅ Create `.dockerignore` file
|
|
4. ✅ Update `packages/frontend/vite.config.ts`
|
|
5. ✅ Create `packages/frontend/.env.production`
|
|
6. ✅ Verify `packages/backend/src/server.ts` configuration
|
|
7. ✅ Create `docker-compose.yml` example
|
|
8. ✅ Create `.env.production.example` template
|
|
|
|
### Phase 2: Build Docker Image (Task 9)
|
|
9. ✅ Run `docker build -t znakovni:latest .`
|
|
10. ✅ Verify image exists with `docker images | grep znakovni`
|
|
|
|
### Phase 3: Optional - Save Image (Task 11)
|
|
11. ⚠️ OPTIONAL: Save image to tar file for transfer
|
|
|
|
### SUCCESS CRITERIA:
|
|
**✅ COMPLETE** when `docker build` succeeds and image `znakovni:latest` exists.
|
|
|
|
User will handle deployment to Unraid.
|
|
|
|
---
|
|
|
|
## Quick Reference: Files to Create/Modify
|
|
|
|
### New Files to Create:
|
|
1. ✅ `Dockerfile` - Multi-stage Docker build
|
|
2. ✅ `docker-entrypoint.sh` - Container startup script (make executable)
|
|
3. ✅ `.dockerignore` - Exclude unnecessary files from build
|
|
4. ✅ `docker-compose.yml` - Example compose configuration
|
|
5. ✅ `.env.production.example` - Production environment template
|
|
6. ✅ `packages/frontend/.env.production` - Frontend production env
|
|
|
|
### Files to Modify:
|
|
1. ⚠️ `packages/frontend/vite.config.ts` - Update host to `0.0.0.0`
|
|
2. ✅ `packages/backend/src/server.ts` - Already correct (verify HOST=0.0.0.0)
|
|
|
|
### Commands to Run After Creating Files:
|
|
```bash
|
|
# Make entrypoint executable
|
|
chmod +x docker-entrypoint.sh
|
|
|
|
# Build the image (THIS IS THE MAIN GOAL)
|
|
docker build -t znakovni:latest .
|
|
|
|
# Verify build success
|
|
docker images | grep znakovni
|
|
|
|
# OPTIONAL: Test run locally
|
|
docker-compose up -d
|
|
|
|
# OPTIONAL: Check logs
|
|
docker-compose logs -f
|
|
```
|
|
|
|
---
|
|
|
|
## Important Implementation Notes for AI Agents
|
|
|
|
### 1. Dockerfile Multi-Stage Build Pattern:
|
|
- **Stage 1 (frontend-builder)**: Build React app with Vite
|
|
- **Stage 2 (backend-builder)**: Build Express app with TypeScript + Prisma
|
|
- **Stage 3 (production)**: Combine built artifacts, minimal runtime
|
|
|
|
### 2. Critical Prisma Considerations:
|
|
- Prisma client MUST be generated during build (`npx prisma generate`)
|
|
- Copy `node_modules/.prisma` directory to production stage
|
|
- Copy `prisma/` directory for migration files
|
|
- Run `prisma migrate deploy` in entrypoint script (NOT `migrate dev`)
|
|
|
|
### 3. Frontend Build Environment:
|
|
- Create `packages/frontend/.env.production` with `VITE_API_URL=http://localhost:3000`
|
|
- This ensures frontend makes API calls to localhost (same container)
|
|
- Vite will embed this at build time
|
|
|
|
### 4. Networking Inside Container:
|
|
- Backend binds to `0.0.0.0:3000` (accessible from frontend)
|
|
- Frontend served on `0.0.0.0:5173` (exposed to host)
|
|
- Both processes run in same container, communicate via localhost
|
|
|
|
### 5. Database Connection:
|
|
- MySQL server is EXTERNAL (not in container)
|
|
- Container must be able to reach MySQL IP (192.168.1.74)
|
|
- Ensure MySQL allows connections from Docker network
|
|
- Test with: `mysql -h 192.168.1.74 -u user -p znakovni`
|
|
|
|
### 6. Volume Mounting for Uploads:
|
|
- Backend uploads to `/app/packages/backend/uploads`
|
|
- Mount host directory to persist files across container restarts
|
|
- Example: `-v /mnt/user/appdata/znakovni/uploads:/app/packages/backend/uploads`
|
|
|
|
### 7. Environment Variables Priority:
|
|
**Required**:
|
|
- `DATABASE_URL` - MySQL connection string
|
|
- `SESSION_SECRET` - Random string (min 32 chars)
|
|
|
|
**Recommended**:
|
|
- `NODE_ENV=production`
|
|
- `PORT=3000`
|
|
- `HOST=0.0.0.0`
|
|
- `FRONTEND_URL=http://localhost:5173`
|
|
|
|
**Optional**:
|
|
- OAuth credentials (if using Google/Microsoft login)
|
|
- `RUN_SEED=true` (to seed database on first run)
|
|
|
|
### 8. Startup Sequence:
|
|
1. Container starts → runs `docker-entrypoint.sh`
|
|
2. Validate environment variables
|
|
3. Run Prisma migrations (`migrate deploy`)
|
|
4. Start backend in background
|
|
5. Wait 5 seconds for backend to initialize
|
|
6. Start frontend server (blocking, keeps container alive)
|
|
7. Trap signals for graceful shutdown
|
|
|
|
### 9. Build Success Checklist:
|
|
- [ ] All files created (Tasks 1-8)
|
|
- [ ] `docker build` command runs without errors
|
|
- [ ] Image `znakovni:latest` appears in `docker images` output
|
|
- [ ] No TypeScript compilation errors
|
|
- [ ] No Prisma generation errors
|
|
- [ ] Multi-stage build completes all 3 stages
|
|
|
|
### 10. Optional Testing Checklist (If Running Container):
|
|
- [ ] Container starts and stays running
|
|
- [ ] Backend health check responds: `curl http://localhost:5173/api/health`
|
|
- [ ] Frontend loads: `curl http://localhost:5173`
|
|
- [ ] Database connection works (check logs)
|
|
- [ ] Uploads persist after container restart
|
|
|
|
### 10. Deployment Flow (For User Reference):
|
|
```bash
|
|
# 1. Build image (AI AGENT TASK - MAIN GOAL)
|
|
docker build -t znakovni:latest .
|
|
|
|
# 2. OPTIONAL: Save image (if deploying to different machine)
|
|
docker save znakovni:latest -o znakovni.tar
|
|
|
|
# 3-7. USER HANDLES DEPLOYMENT
|
|
# - Transfer to Unraid
|
|
# - Load image
|
|
# - Create container
|
|
# - Configure Caddy
|
|
# - Monitor
|
|
```
|
|
|
|
---
|
|
|
|
## End of Guide
|
|
|
|
**GOAL**: Build Docker image `znakovni:latest` successfully.
|
|
|
|
**AI AGENT TASKS**:
|
|
1. Implement Tasks 1-8 (create/modify files)
|
|
2. Execute Task 9 (build image)
|
|
3. Verify image exists
|
|
|
|
**USER RESPONSIBILITY**: Deployment to Unraid and production configuration.
|
|
|
|
This guide provides complete instructions for dockerizing the Znakovni.hr application. Follow the tasks in order, and refer to the troubleshooting section if build issues arise.
|
|
|
|
|