Deployment
Complete guide to deploying ReadySite applications to production.
Overview
ReadySite uses three CLI tools for deployment:
| Command | Purpose |
|---|---|
go run ./cmd/launch |
Create servers and deploy applications |
go run ./cmd/connect |
SSH into servers |
go run ./cmd/publish |
Build and push Docker images to registries |
All configuration lives in infra.json at the project root.
infra.json
The deployment configuration file defines your infrastructure:
{
"platform": {
"provider": "digitalocean",
"token": "$DIGITAL_OCEAN_API_KEY",
"project": "$DIGITAL_OCEAN_PROJECT",
"region": "sfo"
},
"servers": {
"app": {
"size": "small",
"binaries": ["website"]
}
},
"binaries": {
"website": {
"source": "./website",
"ports": [{"host": 80, "container": 5000}],
"volumes": [{"source": "/mnt/data/website", "target": "/data"}],
"env": {
"DB_PATH": "/data/website.db",
"ENV": "production"
},
"env_files": {
"AUTH_SECRET": "/etc/secrets/auth_secret"
}
}
}
}
Platform Section
| Field | Description |
|---|---|
provider |
Cloud provider: digitalocean, aws, gcp, or mock |
token |
API token (supports $ENV_VAR expansion) |
project |
Provider project/account ID |
region |
Default region for new servers |
Servers Section
Each key is a server name. Servers can run binaries and/or services:
"servers": {
"app": {
"size": "small",
"binaries": ["website"],
"services": ["caddy"]
}
}
| Field | Description |
|---|---|
size |
Server size: small, medium, large, xlarge |
binaries |
List of binary names to deploy |
services |
List of infrastructure service names |
Binaries Section
Your applications, built from Go source and deployed as Docker containers:
"binaries": {
"website": {
"source": "./website",
"ports": [{"host": 80, "container": 5000}],
"volumes": [{"source": "/mnt/data/website", "target": "/data"}],
"network": "readysite",
"privileged": false,
"env": {"ENV": "production"},
"env_files": {"AUTH_SECRET": "/etc/secrets/auth_secret"}
}
}
| Field | Description |
|---|---|
source |
Path to Go application directory (must contain Dockerfile) |
ports |
Port mappings (host:container), optional bind for IP |
volumes |
Persistent storage mounts |
network |
Docker network name for service discovery |
privileged |
Enable Docker-in-Docker (for hosting app) |
env |
Environment variables |
env_files |
Env vars read from server files (for secrets) |
Services Section
Pre-built Docker images (reverse proxy, registry, etc.):
"services": {
"caddy": {
"image": "caddy:2-alpine",
"network": "readysite",
"ports": [{"host": 80, "container": 80}, {"host": 443, "container": 443}],
"volumes": [
{"source": "/mnt/data/caddy/data", "target": "/data"},
{"source": "/mnt/data/caddy/config", "target": "/config"},
{"source": "/mnt/data/caddy", "target": "/etc/caddy"}
],
"healthcheck": "curl -sf http://localhost:80/ || exit 1"
}
}
Instances Section
Tracks deployed server instances (managed by launch --new):
"instances": {
"app": [
{"id": "548349326", "name": "website-2", "ip": "134.199.239.73", "region": "sfo3"}
]
}
Dockerfile Pattern
All ReadySite apps use a two-stage build with CGO support:
# Stage 1: Build
FROM golang:1.24-bookworm AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY pkg/ pkg/
COPY website/ website/
RUN go build -o /bin/app ./website
# Stage 2: Runtime
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates curl && \
apt-get clean && rm -rf /var/lib/apt/lists/*
ENV PORT=5000
ENV ENV=production
EXPOSE 5000
COPY --from=builder /bin/app /usr/local/bin/app
CMD ["app"]
Key requirements:
- Build stage: Must use
golang:1.24-bookworm(CGO required for libsql) - Runtime stage: Must use
debian:bookworm-slim(not Alpine — CGO binaries need glibc) - Node.js: Add if using
frontend.WithBundlerfor React islands
With Node.js (for esbuild)
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y curl && \
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
apt-get install -y nodejs && \
apt-get clean && rm -rf /var/lib/apt/lists/*
COPY --from=builder /bin/app /usr/local/bin/app
COPY website/frontend/ frontend/
COPY website/package.json package.json
RUN npm install
CMD ["app"]
Deployment Commands
Create a New Server
go run ./cmd/launch --new app
This will:
- Create a server on your cloud provider
- Wait for SSH access
- Attach volumes
- Build and deploy all configured binaries
- Save the instance to
infra.json
Deploy to All Servers
go run ./cmd/launch
Deploy to a Specific Server
go run ./cmd/launch --server app
Deployment Process
- Volume setup — Ensures persistent volumes are attached and mounted
- Setup scripts — Runs
setup.shif present in the binary's source directory - Build — Builds Docker image locally (or remotely if Docker unavailable)
- Upload — Compresses and transfers image via SSH
- Backup — Backs up running containers for rollback
- Network — Creates Docker networks for service isolation
- Start services — Starts infrastructure services (Caddy, registry)
- Start binaries — Starts application containers with health checks
- Health check — Waits for containers to pass health checks
Health Checks
Containers are configured with automatic health checks:
curl -sf http://localhost:PORT/health || exit 1
- Interval: 10 seconds
- Timeout: 5 seconds
- Retries: 3
- Start period: 10 seconds
SSH Access
# Interactive SSH (auto-selects if single server)
go run ./cmd/connect
# Run a command on the server
go run ./cmd/connect docker logs -f website
# View running containers
go run ./cmd/connect docker ps
Docker Image Publishing
# Build and push to Docker Hub and private registry
go run ./cmd/publish
# Push to Docker Hub only
go run ./cmd/publish --skip-registry
# Custom tag
go run ./cmd/publish --tag v1.2.3
Environment Variables
All Applications
| Variable | Default | Description |
|---|---|---|
PORT |
5000 |
HTTP server port |
ENV |
(empty) | Set to production for prod settings |
DB_PATH |
(empty) | SQLite file path |
DB_URL |
(empty) | Remote libSQL URL |
DB_TOKEN |
(empty) | Remote libSQL token |
AUTH_SECRET |
(empty) | JWT signing secret (required in production) |
TLS/HTTPS
| Variable | Description |
|---|---|
TLS_CERT |
Path to TLS certificate (fullchain.pem) |
TLS_KEY |
Path to TLS private key (privkey.pem) |
When both are set, the app listens on both :PORT (HTTP) and :443 (HTTPS).
Application-Specific
| Variable | App | Description |
|---|---|---|
RESEND_API_KEY |
hosting | Resend email service key |
DIGITAL_OCEAN_API_KEY |
hosting, cmd/launch | DigitalOcean API token |
DIGITAL_OCEAN_PROJECT |
cmd/launch | DigitalOcean project UUID |
TLS/SSL Options
Option 1: Caddy (Recommended)
Use Caddy as a reverse proxy for automatic HTTPS:
# Caddyfile
example.com {
reverse_proxy app:5000
}
Caddy automatically obtains and renews Let's Encrypt certificates.
Option 2: Direct TLS
Set certificate paths in your binary config:
"env": {
"TLS_CERT": "/etc/letsencrypt/live/example.com/fullchain.pem",
"TLS_KEY": "/etc/letsencrypt/live/example.com/privkey.pem"
}
Option 3: Let's Encrypt with certbot
# On the server
apt install certbot
certbot certonly --standalone -d example.com
Setup Scripts
Place a setup.sh in your binary's source directory. It runs on the server before the first deploy:
#!/bin/bash
# website/setup.sh
# Create data directories
mkdir -p /mnt/data/website
# Generate secrets
mkdir -p /etc/secrets
openssl rand -hex 32 > /etc/secrets/auth_secret
Multi-Server Topology
For larger deployments, split services across servers:
{
"servers": {
"web": {
"size": "medium",
"services": ["caddy"],
"binaries": ["website"]
},
"hosting": {
"size": "small",
"binaries": ["hosting"]
}
}
}
Use Docker networks for inter-service communication:
"binaries": {
"website": {"network": "readysite", ...},
"hosting": {"network": "readysite", ...}
}
Database Configuration
Development (In-Memory)
No configuration needed. Data is lost on restart.
go run ./website
Development (Persistent)
DB_PATH=./data/website.db go run ./website
Production (Local File)
"env": {"DB_PATH": "/data/website.db"}
Production (Turso/libSQL Replica)
"env": {
"DB_URL": "libsql://mydb.turso.io",
"DB_TOKEN": "eyJ...",
"DB_PATH": "/data/replica.db"
}
Fast local reads with writes synced to the primary server.