Deploy on a DigitalOcean droplet
This guide is the minimum-viable production-ish setup: one droplet, one service, real DNS, real TLS. Everything here works the same on Hetzner, Vultr, Linode, EC2, or any other cloud that gives you an Ubuntu VM with a public IP.
If you’re on a laptop and just want to kick the tires, use the Quick start instead.
What you need
Section titled “What you need”- A DigitalOcean account (or any cloud that gives you a Linux VM).
- A domain name where you can add an A record. We’ll use
api.example.comin the examples — substitute your own. - ~15 minutes.
Step 1 — create the droplet
Section titled “Step 1 — create the droplet”- Image: Ubuntu 22.04 LTS x64
- Plan: Basic, 1 GB RAM is enough to start
- Region: anywhere
- Authentication: SSH key (recommended)
Note the public IPv4 address. We’ll call it $DROPLET_IP.
Step 2 — point DNS at the droplet
Section titled “Step 2 — point DNS at the droplet”In your DNS provider, add an A record:
api.example.com. IN A $DROPLET_IPACME’s HTTP-01 challenge needs this in place before issuance succeeds. Wait until dig +short api.example.com from your laptop returns the droplet IP. (TTL is usually 5 minutes on a fresh record.)
Step 3 — install Docker
Section titled “Step 3 — install Docker”SSH into the droplet and install Docker from the official repository:
ssh root@$DROPLET_IPapt-get updateapt-get install -y ca-certificates curl gnupg
install -m 0755 -d /etc/apt/keyringscurl -fsSL https://download.docker.com/linux/ubuntu/gpg \ | gpg --dearmor -o /etc/apt/keyrings/docker.gpgchmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \ > /etc/apt/sources.list.d/docker.list
apt-get updateapt-get install -y docker-ce docker-ce-cli containerd.iosystemctl enable --now dockerVerify:
docker run --rm hello-worldStep 4 — install runed
Section titled “Step 4 — install runed”Grab the latest release binary. Replace <VERSION> with the current release tag (or build from source):
RUNE_VERSION=v0.5.0 # check https://github.com/runestack/rune/releasesARCH=$(uname -m)case "$ARCH" in x86_64) ARCH=amd64 ;; aarch64) ARCH=arm64 ;;esac
curl -fsSL "https://github.com/runestack/rune/releases/download/${RUNE_VERSION}/rune_${RUNE_VERSION#v}_linux_${ARCH}.tar.gz" \ | tar -xz -C /usr/local/bin runed rune
# Allow runed to bind low ports (80, 443) without running as root.setcap 'cap_net_bind_service=+ep' /usr/local/bin/runed
mkdir -p /var/lib/rune /etc/runeStep 5 — configure the runefile
Section titled “Step 5 — configure the runefile”Drop a TOML runefile at /etc/rune/runefile.toml:
data_dir = "/var/lib/rune"
[server]grpc_address = ":7863"http_address = ":7861"
[log]level = "info"format = "json"
[networking]cluster_cidr = "10.96.0.0/16"
[telemetry]metrics_addr = "127.0.0.1:9100"
[node]role = "edge"
[acme]# directory = "" # defaults to Let's Encrypt productionStep 6 — run runed as a systemd service
Section titled “Step 6 — run runed as a systemd service”[Unit]Description=Rune serverAfter=network-online.target docker.serviceWants=network-online.target docker.service
[Service]Type=simpleExecStart=/usr/local/bin/runed --config /etc/rune/runefile.tomlRestart=on-failureRestartSec=5sLimitNOFILE=65536
[Install]WantedBy=multi-user.targetsystemctl daemon-reloadsystemctl enable --now runedjournalctl -u runed -fYou should see the ingress + ACME enabled banner:
Ingress + ACME enabled (edge node) http=:80 https=:443acme orchestrator startedStep 7 — bootstrap the admin token
Section titled “Step 7 — bootstrap the admin token”bootstrap is a one-shot, server-enforced local-only operation that mints the root management token. Run it directly on the droplet:
rune context set default --server 127.0.0.1:7863 --token unusedrune admin bootstrap --out-file /var/lib/rune/admin.tokenchmod 600 /var/lib/rune/admin.token
# point the CLI at the just-minted tokenrune context set default \ --server 127.0.0.1:7863 \ --token-file /var/lib/rune/admin.tokenVerify:
$ rune whoamiSubject: rootPolicies: [root]Don’t ship /var/lib/rune/admin.token off the box. For day-to-day use, mint a less-privileged token with rune admin token create.
Step 8 — cast a service
Section titled “Step 8 — cast a service”Create a minimal HTTPS-exposed service. We’ll use nginx so you can see something land in a browser:
service: name: api namespace: default image: nginx:alpine scale: 1 ports: - { name: http, port: 80 } expose: host: api.example.com port: http tls: auto: truerune cast /root/api.yamlrune get servicesStep 9 — watch the certificate land
Section titled “Step 9 — watch the certificate land”$ rune get ingressesNAMESPACE SERVICE HOST TLS CERT EXPIRESdefault api api.example.com acme pending -
# … wait 10–30 seconds …
$ rune get ingressesNAMESPACE SERVICE HOST TLS CERT EXPIRESdefault api api.example.com acme ready 89dFrom your laptop:
curl -I https://api.example.com/# HTTP/2 200# server: rune-ingressThat’s it. You have a single-droplet Rune cluster with:
- automatically-issued, auto-renewing TLS certificates
- service VIPs and embedded DNS for service-to-service traffic
- network policy you can layer on with
rune cast - Prometheus metrics on
127.0.0.1:9100
Hardening checklist
Section titled “Hardening checklist”For real production traffic:
- Firewall. Open
:80and:443to the world, restrict:7863/:7861(gRPC/HTTP API) to your management IPs. - Backups.
data_dir(/var/lib/rune) is the source of truth — snapshot it nightly. - Metrics. Expose
:9100to your scraper only (bind it to a private interface, or front it with a tunnel). - Tokens. Rotate the bootstrap token (
rune admin token rotate) after creating per-user tokens. - Image registries. Configure private-registry auth via
rune admin registry addif you’re pulling from a private repo.
Where to go next
Section titled “Where to go next”- Concepts: Networking — how the pieces fit together.
- Write a network policy — restrict service-to-service traffic.
rune get ingressreference — list and inspect TLS certificate state.runefile.md— every server-side knob.