NextDeploy VPS Deployment Guide: Zero-Downtime Linux Releases Grounded in Go
Deploy Next.js apps to any Linux VPS with zero downtime, Caddy automatic TLS, HMAC-signed socket communication, and a daemon-managed release pipeline. No Docker, no Kubernetes.
Deploying Next.js applications on bare-metal or virtual private servers (Hetzner, DigitalOcean, Linode, AWS EC2, OVH) shouldn't require managing fragile Docker daemon sockets, multi-container compose networks, or manual reverse proxy configurations. NextDeploy deploys Next.js apps directly on Linux using SSH, systemd, and Caddy.
No containers, no Kubernetes, no cloud lock-in — just SSH, systemd, and Caddy driven by a dual-binary architecture.
Architecture: Two Binaries, Two Distinct Roles
NextDeploy divides responsibilities cleanly between your local developer workstation and the target host:
When the CLI triggers a deploy, it SSHes to the server and executes sudo nextdeployd ship --tarball=<remotePath>. The subcommand acts as a client to the running daemon, signing requests with a 32-byte HMAC-SHA256 secret loaded from /etc/nextdeployd/config.json. Replay defense rejects requests with stale timestamps or duplicate nonces.
Quick Start: Deploy in 5 Commands
From a clean Next.js 15+ repository to a production HTTPS release on any Linux VPS:
1# 1. Scaffold nextdeploy.yml manifest
2nextdeploy init
3
4# 2. Provision fresh Linux host in agent mode (Node 22, Caddy, Coraza WAF, daemon)
5nextdeploy prepare
6
7# 3. Securely sync production environment variables
8nextdeploy secrets set DATABASE_URL="postgresql://user:pass@127.0.0.1:5432/app"
9nextdeploy secrets set NEXTAUTH_SECRET="f9b4c2e1a0d8..."
10
11# 4. Build, upload tarball over SSH, and trigger zero-downtime cutover
12nextdeploy ship
13
14# 5. Verify live runtime metrics and stream journalctl logs
15nextdeploy status
16nextdeploy logsThe 5 Steps from Init to Production
1. Project Initialization (nextdeploy init)
Inside your Next.js project root, run nextdeploy init. The CLI inspects package.json for the next dependency, sanitizes your application name into a strict regex slug (^[a-z0-9-]+$, 3–63 chars), and interactively prompts for your deployment topology:
1# Run interactive initializer
2nextdeploy init
3
4# Expected output:
5# ✔ Detected Next.js 15.1.0 in package.json
6# ? Target deployment primitive: VPS (Linux systemd + Caddy)
7# ? Application slug [my-next-app]:
8# ? Target VPS host IP or hostname: 198.51.100.25
9# ? SSH username [ubuntu]:
10# ? SSH private key path [~/.ssh/id_ed25519]:
11# ? Public domain name: app.example.com
12# ✔ Generated nextdeploy.ymlThe generated nextdeploy.yml manifest is minimal, declarative, and version-controlled:
1target_type: vps
2
3app:
4 name: example-app # sanitized slug, NOT a domain
5 environment: production
6 domain: app.example.com # public domain served by Caddy
7 port: 3000 # internal Node.js port
8
9 gosidecar:
10 enabled: false # opt-in Go hyper-server
11 image_optimization: true
12 isr_cache: true
13 max_image_workers: 4
14
15servers:
16 - name: "production-01"
17 host: 198.51.100.25 # your VPS public IP address
18 username: ubuntu # non-root user with passwordless sudo
19 key_path: ~/.ssh/id_ed255192. Server Provisioning (nextdeploy prepare)
Provisioning runs in agent mode: the CLI SSHes to the server, installs the latest nextdeployd static binary from GitHub releases, and runs nextdeployd prepare directly on the target host. No local Ansible or Python dependencies required:
1# Provision default target server in agent mode
2nextdeploy prepare
3
4# Provision a specific server from nextdeploy.yml
5nextdeploy prepare --server production-01
6
7# Provision all configured servers in parallel
8nextdeploy prepare --all
9
10# Advanced provisioning flags:
11nextdeploy prepare --skip-runtimes # Skip Node.js 22 / Corepack if already managed
12nextdeploy prepare --skip-hardening # Skip Fail2Ban jails and logrotate
13nextdeploy prepare --allow-root # Allow provisioning over root SSH session
14nextdeploy prepare --timeout 15m # Override default 10m SSH provisioning timeoutEvery step checks current host state before modifying files, making prepare completely idempotent:
3. Configure Production Secrets (nextdeploy secrets)
Before activating your first build, register environment variables. Secrets are transmitted over encrypted SSH and written to /opt/nextdeploy/secrets/<app>.json with 0600 root-only permissions:
1# Set individual secrets
2nextdeploy secrets set DATABASE_URL="postgresql://user:pass@127.0.0.1:5432/production"
3nextdeploy secrets set NEXTAUTH_SECRET="e9a2b847..."
4nextdeploy secrets set RESEND_API_KEY="re_..."
5
6# Batch-import variables from an existing production env file
7nextdeploy secrets load .env.production
8
9# Audit registered variables (values are masked for security)
10nextdeploy secrets list4. Incremental Build & Packaging (nextdeploy build)
Executing nextdeploy build triggers buildflow.Run. The CLI inspects the current git commit SHA and a cryptographic hash of nextdeploy.yml. If neither changed since the previous artifact, next build is bypassed while release metadata and tarball are fresh-minted:
1# Incremental build (skips next build if git SHA and config hash match)
2nextdeploy build
3
4# Force a full rebuild ignoring build cache locks
5nextdeploy build --force
6
7# Inspect bundle output size and standalone chunks without building
8nextdeploy inspect5. Secure Zero-Downtime Deployment (nextdeploy ship)
Running nextdeploy ship uploads app.tar.gz over SSH to /opt/nextdeploy/uploads/ and calls nextdeployd ship over the local Unix socket. The daemon executes the atomic activateRelease sequence:
1# Deploy to production server
2nextdeploy ship
3
4# Ship with forced clean rebuild
5nextdeploy ship --force
6
7# Deploy to an isolated named environment
8nextdeploy ship -e staging
9
10# Stream verbose execution logs
11nextdeploy ship -v6. Point DNS & Automatic TLS Provisioning
Point your domain's A-record to the VPS IP address. NextDeploy automatically generates dns.md with the exact record configuration needed. On the first incoming HTTPS request, Caddy automatically provisions and manages TLS certificates via Let's Encrypt:
1# DNS Configuration Record:
2# Type: A
3# Host / Name: app (or @ for apex)
4# Value / Target: 198.51.100.25 (Your VPS IP)
5# TTL: 300 (or Auto)
6
7# Test certificate issuance & HTTP/2 handshake
8curl -Iv https://app.example.comDay-2 Operational Commands
Manage production workloads directly from your local terminal:
1# Check active service state, MainPID, and live memory consumption
2nextdeploy status
3
4# Follow application logs via SSH journalctl streaming
5nextdeploy logs
6nextdeploy logs --route /api
7nextdeploy logs --audit # View daemon HMAC command history
8nextdeploy logs --all # Stream app, daemon, and audit logs together
9
10# Hot-reload environment variables without a redeploy
11nextdeploy secrets set DATABASE_URL=postgres://...
12nextdeploy secrets load .env.production
13
14# Instantly revert to previous release
15nextdeploy rollback
16nextdeploy rollback --steps 2
17nextdeploy rollback --to 7c8350a0Infrastructure simplicity is developer independence. True zero downtime requires disciplined, release-pinned state orchestration.