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 buildThe build output lands in .build/. The production start command runs from there:
pnpm run startEnvironment Variables
# 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=trueAlways 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 generateDeployment 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 startupA minimal PM2 ecosystem file:
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:
web: pnpm run startDocker
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"]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
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 hereFile 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.