feat: add Docker support with PostgreSQL and health check endpoint
This commit is contained in:
@@ -0,0 +1,370 @@
|
||||
# Docker Deployment Guide
|
||||
|
||||
This guide covers how to run EuchreCamp using Docker and Docker Compose.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker Engine 20.10+
|
||||
- Docker Compose v2.0+
|
||||
- Make (optional, for using Makefile commands)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Environment Setup
|
||||
|
||||
Create a `.env` file in the project root:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Update the `.env` file with your configuration:
|
||||
|
||||
```env
|
||||
# Database Configuration (PostgreSQL in Docker)
|
||||
DATABASE_PROVIDER=postgresql
|
||||
DATABASE_URL=postgresql://euchre:euchrepassword@postgres:5432/euchre_camp
|
||||
DATABASE_SHADOW_URL=postgresql://euchre:euchrepassword@postgres:5432/euchre_camp_shadow
|
||||
|
||||
# Better Auth (generate a secure secret for production)
|
||||
BETTER_AUTH_SECRET=your-secret-key-change-in-production
|
||||
BETTER_AUTH_URL=http://localhost:3000
|
||||
|
||||
# Application
|
||||
NODE_ENV=production
|
||||
TRUSTED_ORIGINS=http://localhost:3000,http://127.0.0.1:3000
|
||||
```
|
||||
|
||||
### 2. Build and Run
|
||||
|
||||
#### Production Mode
|
||||
|
||||
```bash
|
||||
# Build and start all services
|
||||
docker-compose up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Stop all services
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
#### Development Mode
|
||||
|
||||
```bash
|
||||
# Use development override configuration
|
||||
docker-compose -f docker-compose.yml -f docker-compose.override.yml up -d
|
||||
|
||||
# Or use the shorthand
|
||||
docker-compose --profile dev up -d
|
||||
```
|
||||
|
||||
### 3. Access the Application
|
||||
|
||||
- **Web Application**: http://localhost:3000
|
||||
- **PostgreSQL**: localhost:5432 (default) or 5433 in development
|
||||
|
||||
## Service Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ EuchreCamp │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────────┐ │
|
||||
│ │ Next.js │◄───│ PostgreSQL │ │
|
||||
│ │ App │ │ Database │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ Port: 3000 │ │ Port: 5432 │ │
|
||||
│ └──────────────┘ └──────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Service Details
|
||||
|
||||
### App Service
|
||||
|
||||
- **Image**: Multi-stage Next.js build
|
||||
- **Port**: 3000
|
||||
- **Environment**: Production or Development
|
||||
- **Volumes**:
|
||||
- `app_data`: Persistent storage for uploads
|
||||
|
||||
### PostgreSQL Service
|
||||
|
||||
- **Image**: postgres:15-alpine
|
||||
- **Port**: 5432 (production) / 5433 (development)
|
||||
- **Data Volume**: `postgres_data` (persistent)
|
||||
- **Health Check**: Uses `pg_isready`
|
||||
|
||||
## Database Management
|
||||
|
||||
### View Database Data
|
||||
|
||||
```bash
|
||||
# Access PostgreSQL shell
|
||||
docker exec -it euchre-camp-postgres psql -U euchre -d euchre_camp
|
||||
|
||||
# List tables
|
||||
\dt
|
||||
|
||||
# Exit
|
||||
\q
|
||||
```
|
||||
|
||||
### Backup Database
|
||||
|
||||
```bash
|
||||
# Create backup
|
||||
docker exec euchre-camp-postgres pg_dump -U euchre euchre_camp > backup.sql
|
||||
|
||||
# Restore backup
|
||||
docker exec -i euchre-camp-postgres psql -U euchre euchre_camp < backup.sql
|
||||
```
|
||||
|
||||
### Reset Database
|
||||
|
||||
```bash
|
||||
# Remove all data and restart
|
||||
docker-compose down -v
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Running Commands
|
||||
|
||||
### Run Database Migrations
|
||||
|
||||
```bash
|
||||
# Apply migrations
|
||||
docker-compose exec app npx prisma migrate deploy
|
||||
|
||||
# Generate Prisma client
|
||||
docker-compose exec app npx prisma generate
|
||||
|
||||
# Open Prisma Studio
|
||||
docker-compose exec app npx prisma studio
|
||||
```
|
||||
|
||||
### Create Admin User
|
||||
|
||||
```bash
|
||||
# Create admin user via script
|
||||
docker-compose exec app node scripts/create-admin-via-api.js
|
||||
```
|
||||
|
||||
### Seed Database
|
||||
|
||||
```bash
|
||||
# Run seed script
|
||||
docker-compose exec app node scripts/seed.js
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Hot Reloading
|
||||
|
||||
Development mode supports hot reloading:
|
||||
|
||||
```bash
|
||||
# Start in development mode
|
||||
docker-compose -f docker-compose.yml -f docker-compose.override.yml up -d
|
||||
|
||||
# View logs
|
||||
docker-compose logs -f app
|
||||
```
|
||||
|
||||
### Modifying Code
|
||||
|
||||
Changes to source code are automatically reflected due to volume mounting:
|
||||
|
||||
```bash
|
||||
# Edit a file locally
|
||||
nano src/app/page.tsx
|
||||
|
||||
# Next.js will automatically reload
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**1. Database Connection Failed**
|
||||
|
||||
```bash
|
||||
# Check if PostgreSQL is running
|
||||
docker ps | grep postgres
|
||||
|
||||
# Check logs
|
||||
docker logs euchre-camp-postgres
|
||||
|
||||
# Restart services
|
||||
docker-compose restart postgres
|
||||
```
|
||||
|
||||
**2. Port Already in Use**
|
||||
|
||||
```bash
|
||||
# Check what's using port 3000
|
||||
lsof -i :3000
|
||||
|
||||
# Change port in docker-compose.yml
|
||||
# ports:
|
||||
# - "3001:3000"
|
||||
```
|
||||
|
||||
**3. Permission Denied**
|
||||
|
||||
```bash
|
||||
# Ensure scripts are executable
|
||||
chmod +x scripts/*.sh
|
||||
|
||||
# Check volume permissions
|
||||
docker exec euchre-camp-app ls -la /app
|
||||
```
|
||||
|
||||
**4. Migration Errors**
|
||||
|
||||
```bash
|
||||
# Reset database
|
||||
docker-compose down -v
|
||||
docker-compose up -d
|
||||
|
||||
# Apply migrations manually
|
||||
docker-compose exec app npx prisma migrate deploy
|
||||
```
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# All services
|
||||
docker-compose logs -f
|
||||
|
||||
# Specific service
|
||||
docker-compose logs -f app
|
||||
docker-compose logs -f postgres
|
||||
```
|
||||
|
||||
### Container Shell Access
|
||||
|
||||
```bash
|
||||
# App container
|
||||
docker exec -it euchre-camp-app sh
|
||||
|
||||
# PostgreSQL container
|
||||
docker exec -it euchre-camp-postgres sh
|
||||
```
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Environment Variables
|
||||
|
||||
For production, ensure you set secure values:
|
||||
|
||||
```env
|
||||
# Generate a secure secret
|
||||
BETTER_AUTH_SECRET=$(openssl rand -base64 32)
|
||||
|
||||
# Use real domain
|
||||
BETTER_AUTH_URL=https://euchrecamp.example.com
|
||||
|
||||
# Trusted origins
|
||||
TRUSTED_ORIGINS=https://euchrecamp.example.com,https://www.euchrecamp.example.com
|
||||
```
|
||||
|
||||
### Security Considerations
|
||||
|
||||
1. **Change default PostgreSQL password**:
|
||||
```env
|
||||
POSTGRES_PASSWORD=your-secure-password
|
||||
```
|
||||
|
||||
2. **Use SSL/TLS for database connections**:
|
||||
```env
|
||||
DATABASE_URL=postgresql://euchre:password@postgres:5432/euchre_camp?sslmode=require
|
||||
```
|
||||
|
||||
3. **Restrict network access**:
|
||||
- Don't expose PostgreSQL port to public internet
|
||||
- Use firewall rules to restrict access
|
||||
|
||||
4. **Regular backups**:
|
||||
- Schedule automated database backups
|
||||
- Test restoration process
|
||||
|
||||
### Docker Swarm / Kubernetes
|
||||
|
||||
For production orchestration, consider:
|
||||
|
||||
1. **Separate database service** - Use managed PostgreSQL (RDS, Cloud SQL)
|
||||
2. **Secrets management** - Use Docker secrets or Kubernetes Secrets
|
||||
3. **Health checks** - Configure proper health checks
|
||||
4. **Resource limits** - Set CPU and memory limits
|
||||
5. **Replicas** - Run multiple app instances for high availability
|
||||
|
||||
## Makefile (Optional)
|
||||
|
||||
Create a `Makefile` for convenient commands:
|
||||
|
||||
```makefile
|
||||
.PHONY: up down logs build shell-migrate
|
||||
|
||||
up:
|
||||
docker-compose up -d
|
||||
|
||||
down:
|
||||
docker-compose down
|
||||
|
||||
logs:
|
||||
docker-compose logs -f
|
||||
|
||||
build:
|
||||
docker-compose build --no-cache
|
||||
|
||||
shell-app:
|
||||
docker exec -it euchre-camp-app sh
|
||||
|
||||
shell-db:
|
||||
docker exec -it euchre-camp-postgres psql -U euchre -d euchre_camp
|
||||
|
||||
migrate:
|
||||
docker-compose exec app npx prisma migrate deploy
|
||||
|
||||
seed:
|
||||
docker-compose exec app node scripts/seed.js
|
||||
```
|
||||
|
||||
## Performance Tuning
|
||||
|
||||
### PostgreSQL Configuration
|
||||
|
||||
Edit `docker-compose.yml` to tune PostgreSQL:
|
||||
|
||||
```yaml
|
||||
postgres:
|
||||
# ... other config ...
|
||||
command: >
|
||||
postgres
|
||||
-c max_connections=200
|
||||
-c shared_buffers=256MB
|
||||
-c effective_cache_size=1GB
|
||||
-c maintenance_work_mem=64MB
|
||||
-c checkpoint_completion_target=0.9
|
||||
-c wal_buffers=16MB
|
||||
-c default_statistics_target=100
|
||||
```
|
||||
|
||||
### Next.js Optimization
|
||||
|
||||
The Dockerfile uses multi-stage build for optimal size and performance:
|
||||
|
||||
- **Builder stage**: Installs all dependencies and builds the app
|
||||
- **Runner stage**: Copies only the built artifacts and production dependencies
|
||||
|
||||
## References
|
||||
|
||||
- [Docker Compose Documentation](https://docs.docker.com/compose/)
|
||||
- [PostgreSQL Docker Image](https://hub.docker.com/_/postgres)
|
||||
- [Next.js Deployment](https://nextjs.org/docs/deployment)
|
||||
- [Prisma Deployment](https://www.prisma.io/docs/concepts/components/prisma-migrate/deployment)
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
# Multi-stage build for EuchreCamp Next.js application
|
||||
|
||||
# Stage 1: Builder
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
# Install dependencies
|
||||
RUN apk add --no-cache python3 make g++
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies (including dev dependencies for building)
|
||||
RUN npm ci
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Stage 2: Runner
|
||||
FROM node:20-alpine AS runner
|
||||
|
||||
# Install dumb-init for proper signal handling
|
||||
RUN apk add --no-cache dumb-init
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup --system --gid 1001 euchre && \
|
||||
adduser --system --uid 1001 --ingroup euchre euchre
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy built application from builder
|
||||
COPY --from=builder --chown=euchre:euchre /app/.next ./.next
|
||||
COPY --from=builder --chown=euchre:euchre /app/public ./public
|
||||
COPY --from=builder --chown=euchre:euchre /app/package.json ./package.json
|
||||
COPY --from=builder --chown=euchre:euchre /app/prisma ./prisma
|
||||
|
||||
# Install only production dependencies
|
||||
RUN npm ci --omit=dev
|
||||
|
||||
# Switch to non-root user
|
||||
USER euchre
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
|
||||
|
||||
# Start command
|
||||
ENTRYPOINT ["dumb-init", "--"]
|
||||
CMD ["npm", "start"]
|
||||
@@ -0,0 +1,73 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
target: builder # Use builder stage for development
|
||||
container_name: euchre-camp-app-dev
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
# Database Configuration for Development
|
||||
- DATABASE_PROVIDER=postgresql
|
||||
- DATABASE_URL=postgresql://euchre:euchrepassword@postgres:5432/euchre_camp
|
||||
- DATABASE_SHADOW_URL=postgresql://euchre:euchrepassword@postgres:5432/euchre_camp_shadow
|
||||
|
||||
# Better Auth Configuration
|
||||
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-dev-secret-key}
|
||||
- BETTER_AUTH_URL=${BETTER_AUTH_URL:-http://localhost:3000}
|
||||
|
||||
# Application Configuration
|
||||
- NODE_ENV=development
|
||||
|
||||
# Trusted Origins
|
||||
- TRUSTED_ORIGINS=${TRUSTED_ORIGINS:-http://localhost:3000,http://127.0.0.1:3000}
|
||||
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
# Mount source code for live reload
|
||||
- .:/app
|
||||
- /app/node_modules
|
||||
- /app/.next
|
||||
- app_data:/app/public/uploads
|
||||
|
||||
command: npm run dev
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
- euchre-network
|
||||
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: euchre-camp-postgres-dev
|
||||
environment:
|
||||
- POSTGRES_DB=euchre_camp
|
||||
- POSTGRES_USER=euchre
|
||||
- POSTGRES_PASSWORD=euchrepassword
|
||||
ports:
|
||||
- "5433:5432" # Use different port to avoid conflict with local PostgreSQL
|
||||
volumes:
|
||||
- postgres_data_dev:/var/lib/postgresql/data
|
||||
- ./scripts/init-postgres.sh:/docker-entrypoint-initdb.d/init-postgres.sh
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U euchre -d euchre_camp"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- euchre-network
|
||||
|
||||
volumes:
|
||||
postgres_data_dev:
|
||||
driver: local
|
||||
app_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
euchre-network:
|
||||
driver: bridge
|
||||
@@ -0,0 +1,68 @@
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
target: runner
|
||||
container_name: euchre-camp-app
|
||||
ports:
|
||||
- "3000:3000"
|
||||
environment:
|
||||
# Database Configuration
|
||||
- DATABASE_PROVIDER=postgresql
|
||||
- DATABASE_URL=postgresql://euchre:euchrepassword@postgres:5432/euchre_camp
|
||||
- DATABASE_SHADOW_URL=postgresql://euchre:euchrepassword@postgres:5432/euchre_camp_shadow
|
||||
|
||||
# Better Auth Configuration
|
||||
- BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET:-your-secret-key-change-in-production}
|
||||
- BETTER_AUTH_URL=${BETTER_AUTH_URL:-http://localhost:3000}
|
||||
|
||||
# Application Configuration
|
||||
- NODE_ENV=${NODE_ENV:-production}
|
||||
|
||||
# Trusted Origins for Better Auth
|
||||
- TRUSTED_ORIGINS=${TRUSTED_ORIGINS:-http://localhost:3000,http://127.0.0.1:3000}
|
||||
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
||||
volumes:
|
||||
# Persist uploaded files and static content
|
||||
- app_data:/app/public/uploads
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
- euchre-network
|
||||
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: euchre-camp-postgres
|
||||
environment:
|
||||
- POSTGRES_DB=euchre_camp
|
||||
- POSTGRES_USER=euchre
|
||||
- POSTGRES_PASSWORD=euchrepassword
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
- ./scripts/init-postgres.sh:/docker-entrypoint-initdb.d/init-postgres.sh
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U euchre -d euchre_camp"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- euchre-network
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
driver: local
|
||||
app_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
euchre-network:
|
||||
driver: bridge
|
||||
+6
-1
@@ -13,7 +13,12 @@
|
||||
"test:acceptance:headed": "playwright test src/__tests__/e2e/ --headed",
|
||||
"db:switch": "node scripts/switch-database.js",
|
||||
"db:setup-postgres": "node scripts/setup-postgres.js",
|
||||
"db:seed": "node scripts/seed.js"
|
||||
"db:seed": "node scripts/seed.js",
|
||||
"docker:up": "docker-compose up -d",
|
||||
"docker:down": "docker-compose down",
|
||||
"docker:logs": "docker-compose logs -f",
|
||||
"docker:build": "docker-compose build",
|
||||
"docker:shell": "docker exec -it euchre-camp-app sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# This script runs when the PostgreSQL container is first initialized
|
||||
# It creates the shadow database for Prisma migrations
|
||||
|
||||
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
|
||||
-- Create shadow database for Prisma migrations
|
||||
CREATE DATABASE "euchre_camp_shadow";
|
||||
|
||||
-- Grant privileges
|
||||
GRANT ALL PRIVILEGES ON DATABASE "euchre_camp_shadow" TO "$POSTGRES_USER";
|
||||
EOSQL
|
||||
|
||||
echo "PostgreSQL initialization completed"
|
||||
@@ -0,0 +1,27 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// Check database connection
|
||||
await prisma.$queryRaw`SELECT 1`;
|
||||
|
||||
return NextResponse.json({
|
||||
status: "healthy",
|
||||
database: "connected",
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Health check failed:", error);
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
status: "unhealthy",
|
||||
database: "disconnected",
|
||||
error: "Database connection failed",
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
{ status: 503 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user