Skip to content

Next.js deployment tool - Build, SSH upload, PM2 restart

Notifications You must be signed in to change notification settings

pxilab/nextship

Repository files navigation

PXI NextShip

Next.js deployment tool - Build, SSH upload, PM2 restart.

Installation

# npm
npm install -g @pxilab/nextship

# bun
bun add -g @pxilab/nextship

# Or run directly
npx @pxilab/nextship ship
bunx @pxilab/nextship ship

Quick Start

  1. Configure your Next.js project for standalone output:
// next.config.js
module.exports = {
  output: 'standalone',
}
  1. Create a config file:
cp pxnship.config.example.js pxnship.config.js
  1. Deploy:
pxnship ship

Usage

# Full deployment (build → upload → restart)
pxnship ship

# Individual steps
pxnship build      # Build Next.js application
pxnship upload     # Upload files to server
pxnship restart    # Restart PM2 application

# Options
pxnship ship --dry-run      # Preview without making changes
pxnship ship --skip-build   # Skip build step (use existing build)
pxnship ship --verbose      # Enable detailed output
pxnship ship --config ./custom-config.js  # Use custom config file

Configuration

Config File

Create a config file in your project root. Supported formats:

  • pxnship.config.mjs (recommended - ES modules)
  • pxnship.config.js
  • pxnship.config.ts
  • .pxnshiprc or .pxnshiprc.json

Example pxnship.config.mjs:

export default {
  ssh: {
    host: "server.example.com",
    user: "deploy",
    port: 22,
    privateKeyPath: "~/.ssh/id_ed25519",
  },

  build: {
    command: "bun run build",
    standalone: true,
    prepareLocally: true, // public/ ve static/ dosyalarını local'de standalone içine kopyalar
  },

  upload: {
    remotePath: "/var/www/myapp",
    exclude: [".git", "node_modules", ".env.local"],
  },

  pm2: {
    appName: "myapp",
    ecosystem: true,  // true = auto-detect, false = don't use, "filename.js" = specific file
    reload: true,
    port: 3000,       // Used when ecosystem is false
  },
};

Environment Variables

PXI NextShip loads environment files in the following order (later files override earlier ones):

  1. .env - Base environment variables
  2. .env.local - Local overrides (add to .gitignore)
# .env.local (recommended for local development)
SSH_HOST=server.example.com
SSH_USER=deploy
SSH_PASSWORD=your-password

# Or use SSH key
SSH_KEY_PATH=~/.ssh/id_ed25519

Available Variables:

# Required
SSH_HOST=server.example.com
SSH_USER=deploy

# Authentication (use one)
SSH_KEY=<private-key-content>    # Inline private key (for CI/CD)
SSH_KEY_PATH=~/.ssh/id_ed25519   # Path to private key file
SSH_PASSWORD=<password>          # Password auth

# Optional
SSH_PORT=22
REMOTE_PATH=/var/www/myapp
PM2_APP_NAME=myapp
BUILD_COMMAND="bun run build"

GitHub Actions

name: Deploy

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: oven-sh/setup-bun@v2

      - run: bun install

      - name: Deploy
        run: bunx @pxilab/nextship ship
        env:
          SSH_HOST: ${{ secrets.SSH_HOST }}
          SSH_USER: ${{ secrets.SSH_USER }}
          SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
          PM2_APP_NAME: myapp
          REMOTE_PATH: /var/www/myapp

Server Setup

Directory Structure

/var/www/myapp/
├── .next/
│   └── standalone/
│       ├── .next/
│       │   └── static/     # Build sırasında kopyalanır
│       ├── public/         # Build sırasında kopyalanır
│       ├── package.json    # Next.js standalone build oluşturur
│       └── server.js
└── ecosystem.config.js     # Optional (ayrıca upload edilmeli)

Not: prepareLocally: true (default) kullanıldığında public/ ve .next/static/ klasörleri build sırasında local'de .next/standalone/ içine kopyalanır. Sunucuya sadece .next/standalone/ gönderilir. package.json zaten standalone içinde.

PM2 Configuration

PXI NextShip supports three modes for PM2:

ecosystem value Behavior
true (default) Auto-detect ecosystem.config.js in remote directory
false Don't use ecosystem file, start with server.js directly
"custom.config.js" Use a specific ecosystem file

When ecosystem: false, you can specify a custom port with the port option.

PM2 Environment Variables

Inject environment variables when PM2 starts or reloads:

pm2: {
  appName: "myapp",
  ecosystem: false,  // Standalone mode
  port: 3000,
  env: {
    NODE_ENV: "production",
    API_BASE_URL: process.env.API_BASE_URL,
    DATABASE_URL: process.env.DATABASE_URL,
  },
}

Environment variables are passed to PM2 via shell environment and persist after pm2 save.

Note: When using ecosystem: true, define environment variables in your ecosystem.config.js file instead for better management.

PM2 Ecosystem File (Optional)

// ecosystem.config.js
module.exports = {
  apps: [{
    name: 'myapp',
    script: '.next/standalone/server.js',
    instances: 'max',
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'production',
      PORT: 3000,
    },
  }],
};

Initial Server Setup

# Install PM2
npm install -g pm2

# Create directory
mkdir -p /var/www/myapp
chown deploy:deploy /var/www/myapp

# After first deploy
cd /var/www/myapp
pm2 start ecosystem.config.js  # or: pm2 start .next/standalone/server.js --name myapp
pm2 save
pm2 startup

Platform Support

Platform Transfer Method Performance
Linux / macOS rsync Fast (delta transfer)
Windows + WSL rsync via WSL Fast (delta transfer)
Windows (no rsync) SFTP fallback Slower (full file transfer)

Windows Server Recommendation

For optimal performance on Windows servers, install WSL + rsync:

wsl --install -d Ubuntu
wsl sudo apt update && sudo apt install rsync -y

Without rsync, the tool automatically falls back to SFTP.

Programmatic API

import { loadConfig, runShip, runBuild, runUpload, runRestart } from '@pxilab/nextship';

const config = await loadConfig();

// Full deployment
await runShip(config);

// Or individual steps
await runBuild(config.build);
await runUpload(config.ssh, config.upload);
await runRestart(config.ssh, config.pm2);

License

MIT

About

Next.js deployment tool - Build, SSH upload, PM2 restart

Resources

Stars

Watchers

Forks

Packages

No packages published