Back to Blog
nodejsnextjsdockertutorialdevops

Deploy a Node.js or Next.js App to a VPS with InfraPilot

I

InfraPilot Team

March 8, 2026

When to Move Off Managed Platforms

Vercel and Netlify are excellent for deploying static sites and serverless functions. But once your app needs a persistent database, WebSocket connections, background job queues, or long-running processes, managed platforms become awkward and expensive fast. A VPS with Docker and InfraPilot gives you full control without the serverless constraints.

Containerise Your App

First, add a Dockerfile to your project. For a Next.js app:

FROM node:22-alpine AS builder
WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]

For a plain Node.js/Express app, the Dockerfile is simpler:

FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3000
CMD ["node", "src/index.js"]

The Compose File

Create docker-compose.yml on your VPS:

services:
  app:
    image: ghcr.io/your-org/your-app:latest
    restart: unless-stopped
    environment:
      DATABASE_URL: ${DATABASE_URL}
      NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
      NEXTAUTH_URL: https://app.yourdomain.com
    ports:
      - "3000:3000"
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_DB: appdb
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

Set Up the Reverse Proxy and SSL

In InfraPilot, go to Traffic → Proxy Hosts → Add Proxy Host:

  • Domain: app.yourdomain.com
  • Forward host: app, port: 3000
  • Enable WebSocket support if your app uses real-time features
  • Enable Let's Encrypt SSL and force HTTPS

InfraPilot handles the Nginx config and certificate issuance automatically — no manual certbot or Nginx edits needed.

CI/CD: Auto-Deploy on Push

For automatic deployments, build and push to GitHub Container Registry in your CI pipeline:

# .github/workflows/deploy.yml
- name: Build and push
  run: |
    docker build -t ghcr.io/your-org/your-app:latest .
    docker push ghcr.io/your-org/your-app:latest

- name: Deploy via SSH
  run: |
    ssh user@your-server "cd ~/apps/myapp && docker compose pull && docker compose up -d"

Monitor Your App in InfraPilot

Once deployed, InfraPilot gives you live container metrics, unified logs from the app and database, and alerting if either container restarts unexpectedly. The web terminal lets you run database migrations or debug sessions without leaving your browser.