Docker Deployment
Deploy your AutoDeployBase project using Docker for maximum portability.
Quick Start
Build and Run
# Build the image
docker build -t my-app .
# Run the container
docker run -p 3000:3000 \
-e DATABASE_URL="postgresql://..." \
-e JWT_SECRET="..." \
my-app
Generated Dockerfile
AutoDeployBase generates an optimized Dockerfile:
Dockerfile
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
# Install dependencies
COPY package*.json ./
RUN npm ci
# Copy source
COPY . .
# Generate Prisma client
RUN npx prisma generate
# Build application
RUN npm run build
# Production stage
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# Create non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copy built application
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/prisma ./prisma
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
Docker Compose
Full Stack Setup
docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- '3000:3000'
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- JWT_SECRET=${JWT_SECRET}
- NODE_ENV=production
depends_on:
db:
condition: service_healthy
restart: unless-stopped
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_DB=myapp
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U postgres']
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
restart: unless-stopped
volumes:
postgres_data:
redis_data:
Run with Docker Compose
# Start all services
docker-compose up -d
# Run migrations
docker-compose exec app npx prisma migrate deploy
# View logs
docker-compose logs -f app
# Stop services
docker-compose down
Multi-Stage Builds
Development Stage
# Development
FROM node:20-alpine AS dev
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "dev"]
Production Stage
# Production (optimized)
FROM node:20-alpine AS prod
WORKDIR /app
# Only production dependencies
COPY package*.json ./
RUN npm ci --only=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
CMD ["node", "server.js"]
Environment Configuration
Using .env Files
docker-compose.yml
services:
app:
env_file:
- .env.production
Docker Secrets
docker-compose.yml
services:
app:
secrets:
- db_password
- jwt_secret
environment:
- DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@db:5432/myapp
secrets:
db_password:
file: ./secrets/db_password.txt
jwt_secret:
file: ./secrets/jwt_secret.txt
Health Checks
Application Health
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/api/health || exit 1
Health Endpoint
src/app/api/health/route.ts
import { prisma } from '@/lib/db';
export async function GET() {
try {
// Check database connection
await prisma.$queryRaw`SELECT 1`;
return Response.json({
status: 'healthy',
timestamp: new Date().toISOString(),
database: 'connected'
});
} catch (error) {
return Response.json(
{
status: 'unhealthy',
error: error.message
},
{ status: 503 }
);
}
}
Container Orchestration
Docker Swarm
docker-compose.swarm.yml
version: '3.8'
services:
app:
image: my-app:latest
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
ports:
- '3000:3000'
Deploy:
docker stack deploy -c docker-compose.swarm.yml myapp
Kubernetes
k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
spec:
replicas: 3
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: my-app:latest
ports:
- containerPort: 3000
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url
livenessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /api/health
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
Image Optimization
Size Reduction
# Use Alpine
FROM node:20-alpine
# Clean npm cache
RUN npm cache clean --force
# Remove dev dependencies after build
RUN npm prune --production
Layer Caching
# Copy package files first (cache dependencies)
COPY package*.json ./
RUN npm ci
# Then copy source (changes more often)
COPY . .
Registry & CI/CD
Push to Registry
# Docker Hub
docker tag my-app username/my-app:latest
docker push username/my-app:latest
# GitHub Container Registry
docker tag my-app ghcr.io/username/my-app:latest
docker push ghcr.io/username/my-app:latest
GitHub Actions
.github/workflows/docker.yml
name: Build and Push Docker Image
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: username/my-app:latest
Troubleshooting
Common Issues
Prisma binary not found:
# Ensure Prisma generates for Linux
RUN npx prisma generate
Permission denied:
# Create and use non-root user
RUN adduser --system --uid 1001 appuser
USER appuser
Memory issues:
services:
app:
deploy:
resources:
limits:
memory: 512M
Debugging
# Shell into container
docker exec -it container_name sh
# View logs
docker logs container_name
# Inspect container
docker inspect container_name