Skip to main content

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