Skip to content

Production Deployment

Chuks compiles to a single static binary with no runtime dependencies. This makes deployment straightforward: build once, copy to your server, and run.

Every chuks build produces a production-ready binary, stripped, optimized, and fully static by default.

Terminal window
chuks build

When run inside a project directory (with chuks.json), this reads your entry point and project name automatically:

✓ Successfully compiled src/main.chuks → build/my-app (1.3s)
FlagShortDescription
--output <path>-oCustom output binary path
--os <os>Target OS: linux, darwin, windows
--arch <arch>Target architecture: amd64, arm64
--version <ver>Embed a version string in the binary
Terminal window
chuks build -o myapp

The version from chuks.json is automatically embedded into the binary. Override it with --version:

Terminal window
chuks build --version 2.1.0

This is useful in CI pipelines where the version comes from a Git tag:

Terminal window
chuks build --version $(git describe --tags)

Build for a different platform without any extra tooling:

Terminal window
# Build for Linux server from your Mac
chuks build --os linux --arch amd64
# Build for Windows
chuks build --os windows --arch amd64

Output files are automatically named with the platform suffix:

build/my-app-linux-amd64
build/my-app-windows-amd64.exe

Build for multiple platforms in a single command:

Terminal window
chuks build --os linux --os darwin --os windows --arch amd64
✓ Successfully compiled src/main.chuks → build/my-app-linux-amd64 (1.4s)
✓ Successfully compiled src/main.chuks → build/my-app-darwin-amd64 (1.2s)
✓ Successfully compiled src/main.chuks → build/my-app-windows-amd64.exe (1.5s)
OSArchitectures
linuxamd64, arm64
darwin (macOS)amd64, arm64
windowsamd64, arm64

The binary name is determined by this priority:

  1. -o flag — use exactly what’s specified
  2. chuks.json namebuild/<name> (e.g. build/userservice)
  3. Source filenamebuild/<filename> without the .chuks extension

Cross-compiled binaries get a -<os>-<arch> suffix. Windows binaries always end in .exe.

The simplest deployment: build for Linux and copy the binary to your server.

Terminal window
# Build locally
chuks build --os linux --arch amd64
# Copy to server
scp build/my-app-linux-amd64 user@server:/opt/my-app/my-app
# SSH in and run
ssh user@server
chmod +x /opt/my-app/my-app
./opt/my-app/my-app

The binary has zero dependencies — no runtime, no shared libraries, no package manager installs needed on the server.

For production Linux servers, systemd keeps your app running across reboots and restarts it if it crashes.

Create a service file at /etc/systemd/system/my-app.service:

[Unit]
Description=My Chuks Application
After=network.target
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/my-app
ExecStart=/opt/my-app/my-app
Restart=on-failure
RestartSec=5
StandardOutput=journal
StandardError=journal
# Environment variables
Environment=PORT=3000
Environment=DB_HOST=localhost
# Graceful shutdown (Chuks handles SIGTERM)
KillSignal=SIGTERM
TimeoutStopSec=10
[Install]
WantedBy=multi-user.target

Manage the service:

Terminal window
# Start
sudo systemctl start my-app
# Enable auto-start on boot
sudo systemctl enable my-app
# View logs
journalctl -u my-app -f
# Restart after re-deploying
sudo systemctl restart my-app

Chuks applications handle SIGTERM gracefully — in-flight HTTP requests drain, database connections close, and spawned tasks complete before shutdown.

PM2 is a popular process manager, commonly associated with Node.js, but it works with any binary.

Terminal window
# Install PM2 (if not already installed)
npm install -g pm2
# Start your Chuks app
pm2 start ./build/my-app --name my-app
# View logs
pm2 logs my-app
# Monitor
pm2 monit
# Restart
pm2 restart my-app
# Auto-start on boot
pm2 startup
pm2 save

With an ecosystem file (ecosystem.config.js):

module.exports = {
apps: [
{
name: "my-app",
script: "./build/my-app",
instances: 1,
env: {
PORT: 3000,
DB_HOST: "localhost",
},
env_production: {
PORT: 8080,
DB_HOST: "db.production.internal",
},
},
],
};
Terminal window
pm2 start ecosystem.config.js --env production

Supervisor is another process manager, common on older Linux servers.

Create /etc/supervisor/conf.d/my-app.conf:

[program:my-app]
command=/opt/my-app/my-app
directory=/opt/my-app
user=appuser
autostart=true
autorestart=true
stderr_logfile=/var/log/my-app/error.log
stdout_logfile=/var/log/my-app/output.log
environment=PORT="3000",DB_HOST="localhost"
stopsignal=TERM
stopwaitsecs=10
Terminal window
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start my-app

Since Chuks compiles to a static binary, Docker images can be extremely small.

# Build stage
FROM ubuntu:22.04 AS builder
RUN apt-get update && apt-get install -y curl
RUN curl -fsSL https://raw.githubusercontent.com/chuks-programming-language/releases/main/install.sh | bash
WORKDIR /app
COPY . .
RUN chuks build --os linux --arch amd64
# Runtime stage — minimal image
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /app/build/ /
EXPOSE 3000
ENTRYPOINT ["/my-app-linux-amd64"]

The resulting image is typically 5–15 MB — just the binary and nothing else. No OS, no shell, no package manager.

If you prefer building on your machine:

FROM gcr.io/distroless/static:nonroot
COPY build/my-app-linux-amd64 /app
EXPOSE 3000
ENTRYPOINT ["/app"]
Terminal window
# Build for Linux first
chuks build --os linux --arch amd64
# Build Docker image
docker build -t my-app .
# Run
docker run -p 3000:3000 my-app
services:
app:
build: .
ports:
- "3000:3000"
environment:
- PORT=3000
- DB_HOST=db
depends_on:
- db
restart: unless-stopped
db:
image: postgres:16
environment:
POSTGRES_DB: myapp
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
name: Build & Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Chuks
run: curl -fsSL https://raw.githubusercontent.com/chuks-programming-language/releases/main/install.sh | bash
- name: Build
run: chuks build --os linux --arch amd64 --version ${{ github.sha }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: my-app
path: build/
build:
stage: build
image: ubuntu:22.04
script:
- curl -fsSL https://raw.githubusercontent.com/chuks-programming-language/releases/main/install.sh | bash
- chuks build --os linux --arch amd64 --version $CI_COMMIT_TAG
artifacts:
paths:
- build/

By default, Chuks uses all available CPU cores. On shared servers where Chuks runs alongside other services, you can limit how many cores Chuks uses.

Use the --cpus flag:

Terminal window
chuks run --cpus 4 src/main.chuks
chuks run --cpus=2 src/main.chuks

Use the CHUKS_CPUS environment variable:

Terminal window
CHUKS_CPUS=4 ./build/my-app

This works with all deployment methods:

systemd:

[Service]
Environment=CHUKS_CPUS=4

PM2 ecosystem:

env: {
CHUKS_CPUS: 4,
}

Docker:

environment:
- CHUKS_CPUS=4

Priority: --cpus flag > CHUKS_CPUS env var > default (all cores).

Use the dotenv standard library to load environment variables from .env files in development, and real environment variables in production:

import { dotenv } from "std/dotenv"
import { createServer } from "std/http"
function main() {
dotenv.load()
var port = dotenv.get("PORT","3000")
var dbHost = dotenv.get("DB_HOST","localhost")
var server = createServer()
server.listen(int(port))
}

In production, set environment variables through your deployment method:

MethodHow to set
systemdEnvironment=KEY=value in service file
PM2env block in ecosystem file
Docker-e KEY=value or environment: in compose
CloudPlatform environment variable settings

Production deployments should expose a health endpoint for load balancers and orchestrators:

import { createServer, Request, Response } from "std/http"
function main() {
var server = createServer()
server.get("/health", function(req: Request, res: Response) {
res.json('{"status": "ok"}')
})
// ... your other routes
server.listen(3000)
}

Use this endpoint in your deployment configuration:

Docker Compose:

services:
app:
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
retries: 3

PM2:

Terminal window
pm2 start ./build/my-app --name my-app --health-check-url http://localhost:3000/health

Chuks applications handle shutdown signals (SIGTERM, SIGINT) automatically:

  1. HTTP server stops accepting new connections
  2. In-flight requests are drained (up to 5 seconds)
  3. Spawned tasks run to completion
  4. Database connections are closed
  5. Shutdown hooks are executed in reverse order

This means zero-downtime deployments work out of the box with rolling restarts in Docker, Kubernetes, systemd, or PM2.

WhatCommand
Build for current platformchuks build
Build for Linux serverchuks build --os linux --arch amd64
Build for multiple platformschuks build --os linux --os darwin --os windows
Custom output namechuks build -o myapp
Embed versionchuks build --version 1.0.0
Run with systemdCreate service file, systemctl start
Run with PM2pm2 start ./build/my-app
Run with DockerMulti-stage Dockerfile, 5–15 MB image
Limit CPU cores (VM)chuks run --cpus 4 src/main.chuks
Limit CPU cores (AOT)CHUKS_CPUS=4 ./build/my-app