DEV Community

Cover image for Deploy: App Nextjs + Docker + VPS
Victor Zarzar
Victor Zarzar

Posted on

Deploy: App Nextjs + Docker + VPS

Exemplo mínimo de uso com Bun (baseado na documentação oficial)

Aviso: Este exemplo é puramente acadêmico, baseado na documentação oficial do Next.js. Para um ambiente de produção real, ajustes adicionais de segurança, performance e monitoramento são necessários.

1 - Ajustar o next.config.ts para "Standalone":

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "standalone",
};

export default nextConfig;
Enter fullscreen mode Exit fullscreen mode

2 - Criar um .dockerignore:

############################################################
# Production-ready .dockerignore for a Next.js (Vercel-style) app
# Keeps Docker builds fast, lean, and free of development files.
############################################################

# Dependencies (installed inside Docker, never copied)
node_modules/
.pnpm-store/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

# Next.js build outputs (always generated during `next build`)
.next/
out/
dist/
build/
.vercel/

# Tests and testing output (not needed in production images)
coverage/
.nyc_output/
__tests__/
__mocks__/
jest/
cypress/
cypress/screenshots/
cypress/videos/
playwright-report/
test-results/
.vitest/
vitest.config.*
jest.config.*
cypress.config.*
playwright.config.*
*.test.*
*.spec.*

# Local development and editor files
.git/
.gitignore
.gitattributes
.vscode/
.idea/
*.swp
*.swo
*~
*.log

# Environment variables (only commit template files)
.env
.env*.local
.env.development
.env.test
.env.production.local

# Docker configuration files (not needed inside build context)
Dockerfile*
.dockerignore
compose.yaml
compose.yml
docker-compose*.yaml
docker-compose*.yml

# Documentation
*.md
docs/

# CI/CD configuration files
.github/
.gitlab-ci.yml
.travis.yml
.circleci/
Jenkinsfile

# Cache directories and temporary data
.cache/
.parcel-cache/
.eslintcache
.stylelintcache
.swc/
.turbo/
.tmp/
.temp/

# TypeScript build metadata
*.tsbuildinfo

# Sensitive or unnecessary configuration files
*.pem
.editorconfig
.prettierrc*
prettier.config.*
.eslintrc*
eslint.config.*
.stylelintrc*
stylelint.config.*
.babelrc*
*.iml
*.ipr
*.iws

# OS-specific junk
.DS_Store
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
Desktop.ini
Enter fullscreen mode Exit fullscreen mode

3 - Criar o Dockerfile:

# ============================================
# Stage 1: Dependencies Installation Stage
# ============================================

# This Dockerfile.bun is specifically configured for projects using Bun
# For npm/pnpm or yarn, refer to the Dockerfile instead

FROM oven/bun:1 AS dependencies

# Set working directory
WORKDIR /app

# Copy package-related files first to leverage Docker's caching mechanism
COPY package.json bun.lock* ./

# Install project dependencies with frozen lockfile for reproducible builds
RUN --mount=type=cache,target=/root/.bun/install/cache \
    bun install --no-save --frozen-lockfile

# ============================================
# Stage 2: Build Next.js application in standalone mode
# ============================================

FROM oven/bun:1 AS builder

# Set working directory
WORKDIR /app

# Copy project dependencies from dependencies stage
COPY --from=dependencies /app/node_modules ./node_modules

# Copy application source code
COPY . .

ENV NODE_ENV=production

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED=1

# Build Next.js application
RUN bun run build

# ============================================
# Stage 3: Run Next.js application
# ============================================

FROM oven/bun:1 AS runner

# Set working directory
WORKDIR /app

# Set production environment variables
ENV NODE_ENV=production
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the run time.
# ENV NEXT_TELEMETRY_DISABLED=1

# Copy production assets
COPY --from=builder --chown=bun:bun /app/public ./public

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown bun:bun .next

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=bun:bun /app/.next/standalone ./
COPY --from=builder --chown=bun:bun /app/.next/static ./.next/static

# If you want to persist the fetch cache generated during the build so that
# cached responses are available immediately on startup, uncomment this line:
# COPY --from=builder --chown=bun:bun /app/.next/cache ./.next/cache

# Switch to non-root user for security best practices
USER bun

# Expose port 3000 to allow HTTP traffic
EXPOSE 3000

# Start Next.js standalone server with Bun
CMD ["bun", "server.js"]
Enter fullscreen mode Exit fullscreen mode

4 - Criar o Docker compose:

# Bun service (use with: docker compose up nextjs-standalone-with-bun --build)
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: nextjs-standalone-bun-image
    container_name: nextjs-app
    environment:
      NODE_ENV: production
      PORT: "3000"
      HOSTNAME: "0.0.0.0"
    expose:
      - "3000"
    restart: unless-stopped
    networks:
      - app-network

  nginx:
    image: nginx:stable-alpine
    container_name: nginx-proxy
    depends_on:
      - app
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/default.conf:/etc/nginx/conf.d/nginx.conf:ro
      - certbot_www:/var/www/certbot
      - certbot_conf:/etc/letsencrypt
    restart: unless-stopped
    networks:
      - app-network

  certbot:
    image: certbot/certbot:latest
    container_name: certbot
    volumes:
      - certbot_www:/var/www/certbot
      - certbot_conf:/etc/letsencrypt
    entrypoint: >
      sh -c 'trap exit TERM; while :; do certbot renew --webroot -w /var/www/certbot --quiet; sleep 12h; done'
    restart: unless-stopped

volumes:
  certbot_www:
  certbot_conf:

networks:
  app-network:
    driver: bridge
Enter fullscreen mode Exit fullscreen mode

5 - Criar um template básico do nginx:

server {
  listen 80;
  server_name seudominio.com www.seudominio.com;

  location /.well-known/acme-challenge/ {
    root /var/www/certbot;
  }

  location / {
    proxy_pass http://app:3000;
    proxy_http_version 1.1;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
  }
}
Enter fullscreen mode Exit fullscreen mode

6 - Buildar a imagem Docker e Subir o container:

Automatizado:

make build && make up
Enter fullscreen mode Exit fullscreen mode

Manual:

docker compose build
docker compose up -d
Enter fullscreen mode Exit fullscreen mode

7 - Opcional - Criar um Makefile mínimo para automatizar o processo:

# Makefile - App Nextjs + Docker + VPS Example
PROJECT_NAME=App Nextjs
DOCKER_IMAGE_NAME=app-next-js
DOCKER_CONTAINER_NAME=app-next-js
COMPOSE_FILE=docker-compose.yaml

build:
    docker compose -f $(COMPOSE_FILE) build

up:
    docker compose -f $(COMPOSE_FILE) up -d

stop:
    docker compose -f $(COMPOSE_FILE) stop

clean: stop
    docker compose -f $(COMPOSE_FILE) down --remove-orphans
    docker rmi -f $(DOCKER_IMAGE_NAME) >/dev/null 2>&1 || true
    rm -rf node_modules .next >/dev/null 2>&1 || true
Enter fullscreen mode Exit fullscreen mode

Conclusão

Com essa arquitetura mínima, você tem uma aplicação Next.js rodando em modo standalone dentro de um container Docker, servida pelo Nginx como proxy reverso e com certificado SSL automatizado via Certbot — tudo orquestrado pelo Docker Compose em uma VPS.

O Makefile opcional simplifica o dia a dia com comandos curtos para build, subir e limpar o ambiente. É uma base funcional e direta, sem complexidade desnecessária.

Lembre-se: este exemplo é um ponto de partida acadêmico. Para produção real, considere variáveis de ambiente seguras, healthchecks, logging, CI/CD e hardening do servidor.

Top comments (0)