Getting Started

Deployment

Deploying an Arkos.js application is the same as deploying any standard Node.js application. This guide covers the build process and common deployment setups.

Prerequisites

Before deploying, make sure you have:

  • Node.js 22.9+ on your production environment
  • All environment variables configured
  • A database accessible from the production server

Building

pnpm install
pnpm run build

The build output lands in .build/. The production start command runs from there:

pnpm run start

Environment Variables

.env
# Required
DATABASE_URL=your-production-database-url
JWT_SECRET=your-production-jwt-secret

# Recommended
PORT=8000
HOST=0.0.0.0
NODE_ENV=production
JWT_COOKIE_SECURE=true
JWT_COOKIE_HTTP_ONLY=true

Always set a strong JWT_SECRET in production. Arkos throws on login attempts when no secret is configured.

Database

Run migrations before starting the app:

npx prisma migrate deploy
npx prisma generate

Deployment Platforms

VPS (DigitalOcean, Hetzner, Contabo, AWS EC2)

# Install dependencies and build
pnpm install
pnpm run build

# Run with PM2
npm install -g pm2

pm2 start pnpm --name "my-app" -- run start
pm2 save
pm2 startup

A minimal PM2 ecosystem file:

ecosystem.config.js
module.exports = {
  apps: [{
    name: "my-app",
    script: "pnpm",
    args: ["run", "start"],
    instances: "max",
    exec_mode: "cluster",
    env: {
      NODE_ENV: "production",
    },
  }],
};

Platform as a Service (Railway, Render, Heroku)

Set your environment variables in the platform dashboard, then point the start command at pnpm run start. For Heroku, add a Procfile:

Procfile
web: pnpm run start

Docker

Dockerfile
FROM node:22-alpine

WORKDIR /app

COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install

COPY . .

RUN pnpm run build
RUN npx prisma generate

EXPOSE 8000

CMD ["pnpm", "run", "start"]
docker-compose.yml
version: "3.8"
services:
  app:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/arkos
      - JWT_SECRET=your-jwt-secret
      - NODE_ENV=production
    depends_on:
      - db

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_DB=arkos
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Nginx Reverse Proxy

server {
    listen 80;
    server_name your-domain.com;

    location / {
        proxy_pass http://localhost:8000;
        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-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_cache_bypass $http_upgrade;
    }
}

Set app.set("trust proxy", 1) in src/app.ts when running behind a reverse proxy so that rate limiting and IP-based features work correctly.

CI/CD Example

.github/workflows/deploy.yml
name: Deploy

on:
  push:
    branches: [main]

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

      - uses: actions/setup-node@v4
        with:
          node-version: "22"

      - name: Install pnpm
        uses: pnpm/action-setup@v3
        with:
          version: latest

      - name: Install and build
        run: |
          pnpm install
          pnpm run build

      - name: Run tests
        run: pnpm test

      - name: Deploy
        # Add your deployment step here

File Uploads in Production

Local file storage works fine for single-server deployments. For multi-instance or containerized setups, use cloud storage (S3, Cloudinary, R2) instead — files written to the local filesystem won't be shared across instances.