readysite / docs / deployment.md
9.2 KB
deployment.md

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.WithBundler for 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:

  1. Create a server on your cloud provider
  2. Wait for SSH access
  3. Attach volumes
  4. Build and deploy all configured binaries
  5. 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

  1. Volume setup — Ensures persistent volumes are attached and mounted
  2. Setup scripts — Runs setup.sh if present in the binary's source directory
  3. Build — Builds Docker image locally (or remotely if Docker unavailable)
  4. Upload — Compresses and transfers image via SSH
  5. Backup — Backs up running containers for rollback
  6. Network — Creates Docker networks for service isolation
  7. Start services — Starts infrastructure services (Caddy, registry)
  8. Start binaries — Starts application containers with health checks
  9. 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.

← Back