Deploying Next.js and PostgreSQL with Docker

4 Nov 2026, 11:2715 min readby ITYantra Admin

Complete guide to deploying Next.js and PostgreSQL applications with Docker including multi-container setup, docker-compose, environment variables, and production deployment.

# Deploying Next.js and PostgreSQL with Docker

Introduction

Deploying a Next.js application with PostgreSQL using Docker involves containerizing both the frontend/backend application and the database, connecting them via Docker networking, and managing environment variables and persistent data. This tutorial provides a complete production-ready setup.

Learning Objectives

By the end of this tutorial, you will be able to:

  • Containerize a Next.js application
  • Run PostgreSQL in a Docker container
  • Connect Next.js to PostgreSQL via Docker networking
  • Use docker-compose for multi-container orchestration
  • Manage environment variables and secrets
  • Configure persistent data for PostgreSQL
  • Deploy to production

Technical Background

Architecture

Next.js Container (port 3000)
    |
    | (Docker network: app-network)
    v
PostgreSQL Container (port 5432)
    |
    v
Named Volume: postgres_data

Prerequisites

  • Docker and Docker Compose installed
  • Next.js application code
  • Understanding of environment variables

Hardware and Software Requirements

| Component | Requirement | |-----------|-------------| | Docker | v24+ | | Docker Compose | v2+ | | Node.js | v20+ (for local development) |

Step-by-Step Implementation

Phase 1: Next.js Dockerfile

# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup -S nodejs && adduser -S nextjs -G nodejs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]

Phase 2: Docker Compose Configuration

# docker-compose.yml
version: '3.8'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/mydb
      - NEXTAUTH_SECRET=${NEXTAUTH_SECRET}
      - NEXTAUTH_URL=http://localhost:3000
    depends_on:
      db:
        condition: service_healthy
    networks:
      - app-network
    restart: unless-stopped
  
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data
    ports:
      - "5432:5432"
    networks:
      - app-network
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped
  
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    networks:
      - app-network
    restart: unless-stopped

networks:
  app-network:
    driver: bridge

volumes:
  postgres_data:

Phase 3: Environment Configuration

# .env file
NEXTAUTH_SECRET=your-secret-key-here
NEXTAUTH_URL=http://localhost:3000
DATABASE_URL=postgresql://postgres:password@db:5432/mydb
# Load environment variables
export $(cat .env | xargs)

# Or use with docker compose
docker compose --env-file .env up -d

Phase 4: Next.js Configuration

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'standalone',
  reactStrictMode: true,
}

module.exports = nextConfig
// lib/db.ts
import { Pool } from 'pg'

const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
})

export async function query(text: string, params?: any[]) {
  const client = await pool.connect()
  try {
    const result = await client.query(text, params)
    return result
  } finally {
    client.release()
  }
}

Phase 5: Deploy with Docker Compose

# Build and start all services
docker compose up -d --build

# View logs
docker compose logs -f app

# Check service health
docker compose ps

# Stop all services
docker compose down

# Stop and remove volumes
docker compose down -v

Phase 6: Database Migrations

# Run migrations inside app container
docker compose exec app npx prisma migrate deploy

# Or run standalone migration
docker compose run --rm app npx prisma migrate deploy

Phase 7: Production Deployment

# docker-compose.prod.yml
version: '3.8'
services:
  app:
    image: myregistry.com/myapp:prod
    environment:
      - NODE_ENV=production
      - DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@db:5432/mydb
    secrets:
      - db_password
      - nextauth_secret
    deploy:
      replicas: 2
      update_config:
        parallelism: 1
        delay: 10s
      restart_policy:
        condition: on-failure
  
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password
    volumes:
      - postgres_data:/var/lib/postgresql/data

secrets:
  db_password:
    file: ./secrets/db_password
  nextauth_secret:
    file: ./secrets/nextauth_secret

volumes:
  postgres_data:
# Deploy with production config
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Expected Output

  • Next.js application containerized and running
  • PostgreSQL database with persistent data
  • Services communicating via Docker network
  • Environment variables configured
  • Production deployment working

Verification Procedures

| Check | Command | Expected Result | |-------|---------|-----------------| | Services running | `docker compose ps` | All services healthy | | App accessible | `curl http://localhost:3000` | HTTP 200 | | Database connected | `docker compose exec app npx prisma db execute "SELECT 1"` | Query succeeds | | Logs | `docker compose logs app` | No errors |

Troubleshooting Scenarios

| Issue | Diagnosis | Resolution | |-------|-----------|------------| | App can't connect to DB | Wrong DATABASE_URL or DB not ready | Verify env var; check DB healthcheck | | Build fails | Missing dependencies or build error | Check Dockerfile; verify package.json | | Data lost on restart | No volume for PostgreSQL | Add postgres_data volume | | Container exits immediately | Application crash | Check container logs |

Best Practices

  • **Use standalone output:** Next.js standalone for smaller Docker images
  • **Health checks:** Configure PostgreSQL healthcheck
  • **Persistent volumes:** Use named volumes for database data
  • **Environment variables:** Keep secrets out of code; use .env or Docker secrets
  • **Non-root user:** Run Next.js as non-root user
  • **Resource limits:** Set memory and CPU limits in production

Security Considerations

  • **Database password:** Use Docker secrets or environment variables, not hardcoded
  • **NEXTAUTH_SECRET:** Use strong random secret
  • **Non-root user:** Run containers as non-root
  • **Network isolation:** Use custom Docker network
  • **Image scanning:** Scan for vulnerabilities before deployment

Conclusion

Deploying Next.js and PostgreSQL with Docker provides a portable, reproducible deployment pattern. By containerizing both the application and database, using docker-compose for orchestration, and following security best practices, you can run full-stack applications reliably in any environment.

References

  • Next.js Docker: https://nextjs.org/docs/deployment#docker-contai
  • PostgreSQL Docker: https://hub.docker.com/_/postgres
  • Docker Compose: https://docs.docker.com/compose/
  • Next.js Standalone: https://nextjs.org/docs/advanced-features/output-file-tracing
#containerization#deployment#fullstack#docker-compose#postgresql#nextjs#Docker