• Home
  • Cloud VPS
    • Hong Kong VPS
    • US VPS
  • Dedicated Servers
    • Hong Kong Servers
    • US Servers
    • Singapore Servers
    • Japan Servers
  • Company
    • Contact Us
    • Blog
logo logo
  • Home
  • Cloud VPS
    • Hong Kong VPS
    • US VPS
  • Dedicated Servers
    • Hong Kong Servers
    • US Servers
    • Singapore Servers
    • Japan Servers
  • Company
    • Contact Us
    • Blog
ENEN
  • 简体简体
  • 繁體繁體
Client Area

Deploy Next.js on Hong Kong VPS: Full-Stack TypeScript Production Guide (2026)

September 2, 2026

Next.js 14 with the App Router is the dominant full-stack React framework — combining server-side rendering, static generation, API routes, and server actions in one codebase. Deployed on a Hong Kong VPS with CN2 GIA routing, a Next.js application serves server-rendered pages and API responses to mainland Chinese users at 30–60ms — far better than Vercel’s US or EU infrastructure, with no per-seat or per-invocation pricing.


Why Self-Host Next.js Instead of Vercel?

  • China accessibility — Vercel’s edge network is not optimised for mainland China; your HK VPS with CN2 GIA delivers dramatically better TTFB for Chinese users
  • Cost at scale — Vercel Pro costs $20/month per team member plus function invocation fees; a VPS is a flat rate regardless of traffic
  • Full server access — run PostgreSQL, Redis, and cron jobs on the same server; no need for external database services
  • No function timeout limits — Vercel limits serverless functions to 10–60 seconds; VPS has no such constraint for long-running operations

Step 1: Install Node.js and PM2

<code">apt update && apt upgrade -y
curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
apt install -y nodejs nginx certbot python3-certbot-nginx ufw

npm install -g pm2 typescript

node --version   # v20.x
npm --version

Step 2: Configure Next.js for Self-Hosting

<code"># next.config.ts — key settings for self-hosted production
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  // Required for standalone output (single deployable folder)
  output: 'standalone',

  // Enable React strict mode
  reactStrictMode: true,

  // Image optimisation — allow your domain
  images: {
    domains: ['yourdomain.com', 'cdn.yourdomain.com'],
    formats: ['image/avif', 'image/webp'],
  },

  // Headers for security and caching
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          { key: 'X-Frame-Options', value: 'DENY' },
          { key: 'X-Content-Type-Options', value: 'nosniff' },
          { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
        ],
      },
      {
        // Cache static assets for 1 year
        source: '/_next/static/(.*)',
        headers: [{ key: 'Cache-Control', value: 'public, max-age=31536000, immutable' }],
      },
    ]
  },

  // Redirect www to non-www (or vice versa)
  async redirects() {
    return [
      {
        source: '/:path*',
        has: [{ type: 'host', value: 'www.yourdomain.com' }],
        destination: 'https://yourdomain.com/:path*',
        permanent: true,
      },
    ]
  },
}

export default nextConfig

Step 3: Environment Variables

<code"># .env.production (on VPS — never commit to git)
DATABASE_URL=postgresql://nextjs_user:Password!@localhost/nextjs_db
NEXTAUTH_URL=https://yourdomain.com
NEXTAUTH_SECRET=GENERATE_64_CHAR_SECRET_HERE
REDIS_URL=redis://:RedisPass@localhost:6379/0

# For server components and API routes only (not exposed to browser):
DATABASE_URL=...

# For client-side use (prefixed with NEXT_PUBLIC_):
NEXT_PUBLIC_API_URL=https://yourdomain.com/api
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX

Step 4: Build for Production

<code"># On your VPS (or build locally and transfer)
useradd -m -s /bin/bash nextjs
mkdir -p /var/www/myapp
chown nextjs:nextjs /var/www/myapp

su - nextjs
cd /var/www/myapp

git clone https://github.com/your-org/myapp.git .
cp /path/to/.env.production .env.production

npm ci --production=false   # Install ALL deps for build
npm run build               # Creates .next/standalone/

# The standalone folder is self-contained
ls .next/standalone/
# server.js  node_modules/  .next/  public/  package.json

Step 5: PM2 Process Manager

<code">cat > /var/www/myapp/ecosystem.config.js << 'EOF'
module.exports = {
  apps: [{
    name: 'nextjs',
    cwd: '/var/www/myapp/.next/standalone',
    script: 'server.js',
    instances: 'max',        // One worker per CPU core
    exec_mode: 'cluster',    // Load-balanced cluster mode
    env_production: {
      NODE_ENV: 'production',
      PORT: 3000,
      HOSTNAME: '127.0.0.1',
    },
    max_memory_restart: '1G',
    error_file: '/var/log/nextjs/error.log',
    out_file: '/var/log/nextjs/out.log',
    merge_logs: true,
    time: true,
    // Graceful shutdown
    kill_timeout: 5000,
    listen_timeout: 10000,
  }]
}
EOF

mkdir -p /var/log/nextjs
chown nextjs:nextjs /var/log/nextjs

pm2 start /var/www/myapp/ecosystem.config.js --env production
pm2 save

# Auto-start on server reboot
pm2 startup systemd -u nextjs --hp /home/nextjs
# Run the generated command as root

Step 6: Nginx Reverse Proxy

<code">cat > /etc/nginx/sites-available/nextjs << 'EOF'
upstream nextjs {
    server 127.0.0.1:3000;
    keepalive 64;
}

# Nginx-level caching for static assets
proxy_cache_path /var/cache/nginx/nextjs
    levels=1:2
    keys_zone=nextjs_cache:10m
    max_size=1g
    inactive=60m;

server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;

    # Next.js static files — cache at Nginx level
    location /_next/static/ {
        alias /var/www/myapp/.next/standalone/.next/static/;
        expires 1y;
        add_header Cache-Control "public, immutable";
        access_log off;
    }

    # Public folder (images, favicon, etc.)
    location /public/ {
        alias /var/www/myapp/.next/standalone/public/;
        expires 30d;
        add_header Cache-Control "public";
    }

    # All other requests → Next.js
    location / {
        proxy_pass http://nextjs;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        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_cache_bypass $http_upgrade;
        proxy_read_timeout 60s;
    }
}
EOF

mkdir -p /var/cache/nginx/nextjs
ln -s /etc/nginx/sites-available/nextjs /etc/nginx/sites-enabled/
certbot --nginx -d yourdomain.com
nginx -t && systemctl reload nginx

Step 7: Database — Drizzle ORM with PostgreSQL

<code"># Install Drizzle ORM (recommended for Next.js + TypeScript)
npm install drizzle-orm pg drizzle-kit @types/pg

# db/schema.ts
import { pgTable, uuid, text, timestamp, boolean } from 'drizzle-orm/pg-core'

export const users = pgTable('users', {
  id: uuid('id').defaultRandom().primaryKey(),
  email: text('email').notNull().unique(),
  name: text('name').notNull(),
  createdAt: timestamp('created_at').defaultNow(),
  active: boolean('active').default(true),
})

export const posts = pgTable('posts', {
  id: uuid('id').defaultRandom().primaryKey(),
  title: text('title').notNull(),
  slug: text('slug').notNull().unique(),
  content: text('content'),
  authorId: uuid('author_id').references(() => users.id),
  publishedAt: timestamp('published_at'),
})

# db/index.ts
import { drizzle } from 'drizzle-orm/node-postgres'
import { Pool } from 'pg'

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

export const db = drizzle(pool)

Usage in Server Components

<code"># app/posts/page.tsx — Server Component (runs on VPS, not in browser)
import { db } from '@/db'
import { posts, users } from '@/db/schema'
import { desc, eq } from 'drizzle-orm'

export default async function PostsPage() {
  // This query runs on the VPS — direct DB connection, no API overhead
  const allPosts = await db
    .select({
      title: posts.title,
      slug: posts.slug,
      author: users.name,
      publishedAt: posts.publishedAt,
    })
    .from(posts)
    .leftJoin(users, eq(posts.authorId, users.id))
    .orderBy(desc(posts.publishedAt))
    .limit(20)

  return (
    • {allPosts.map(post => (

    • {post.title} — {post.author}

))}

) }


Step 8: Zero-Downtime Deployment Script

<code"># deploy.sh — run from your local machine or CI/CD
#!/bin/bash
set -e

VPS_HOST="root@YOUR_VPS_IP"
APP_DIR="/var/www/myapp"

echo "Building Next.js..."
npm run build

echo "Transferring standalone build..."
rsync -az --delete \
  .next/standalone/ \
  $VPS_HOST:$APP_DIR/.next/standalone/

rsync -az --delete \
  .next/static/ \
  $VPS_HOST:$APP_DIR/.next/standalone/.next/static/

rsync -az --delete \
  public/ \
  $VPS_HOST:$APP_DIR/.next/standalone/public/

echo "Running migrations..."
ssh $VPS_HOST "cd $APP_DIR && npm run db:migrate"

echo "Reloading PM2 (zero-downtime)..."
ssh $VPS_HOST "pm2 reload nextjs --update-env"

echo "Verifying..."
sleep 3
ssh $VPS_HOST "curl -sf http://127.0.0.1:3000/api/health && echo 'OK'"
echo "Deployment complete"

Performance Benchmarks: Next.js on HK VPS vs Vercel

MetricVercel (US/EU edge)HK VPS CN2 GIA
TTFB from Shanghai (SSR page)180–350ms35–65ms
TTFB from Shanghai (static)80–150ms15–30ms
TTFB from Hong Kong60–120ms8–20ms
Monthly cost (medium traffic)$20–100+Flat VPS rate
DB query latency (on same server)N/A (external DB)0.5–2ms

Conclusion

Next.js deployed on a Hong Kong VPS with PM2 cluster mode, Nginx reverse proxy, and PostgreSQL on the same server delivers full-stack TypeScript applications to mainland Chinese users at 35–65ms TTFB — 3–5× faster than Vercel’s international infrastructure. The standalone build mode creates a self-contained deployable folder; PM2’s cluster mode utilises all CPU cores; and Nginx caches static assets locally, making the architecture both fast and cost-predictable.

Deploy your Next.js app: Browse Server.HK Hong Kong VPS plans — a 4 GB plan handles Next.js with PM2 cluster, PostgreSQL, and Redis for most production workloads.

Leave a Reply

You must be logged in to post a comment.

Recent Posts

  • Self-Hosted Private Search Engine on Hong Kong VPS: SearXNG (2026)
  • WireGuard Site-to-Site VPN with Hong Kong VPS: Connect Your Offices (2026)
  • Deploy Elixir Phoenix on Hong Kong VPS: Real-Time Apps for Asia (2026)
  • GPU Dedicated Server in Hong Kong: AI Model Training and Inference (2026)
  • Vector Database on Hong Kong VPS: Build a RAG System with pgvector (2026)

Recent Comments

  1. Hong Kong VPS Uptime and SLA: What 99.9% Uptime Really Means for Your Business (2026) - Server.HK on How to Monitor Your Hong Kong VPS: Uptime, Performance, and Alert Setup Guide (2026)
  2. Best Hong Kong VPS Providers in 2026: Compared by Speed, Routing, and Value - Server.HK on How to Migrate Your Website to a Hong Kong VPS: Zero-Downtime Transfer Guide (2026)
  3. vibramycin injection on How to Choose the Right Hong Kong VPS Plan: A Buyer’s Guide for 2026
  4. allopurinol for gout on CN2 GIA vs BGP vs CN2 GT: What’s the Real Difference for China Connectivity?
  5. antibiotics online purchase on How to Set Up a WordPress Site on a Hong Kong VPS with aaPanel (Step-by-Step 2026)

Knowledge Base

Access detailed guides, tutorials, and resources.

Live Chat

Get instant help 24/7 from our support team.

Send Ticket

Our team typically responds within 10 minutes.

logo
Alipay Cc-paypal Cc-stripe Cc-visa Cc-mastercard Bitcoin
Cloud VPS
  • Hong Kong VPS
  • US VPS
Dedicated Servers
  • Hong Kong Servers
  • US Servers
  • Singapore Servers
  • Japan Servers
More
  • Contact Us
  • Blog
  • Legal
© 2026 Server.HK | Hosting Limited, Hong Kong | Company Registration No. 77008912
Telegram
Telegram @ServerHKBot