terainia/DEPLOY.md
Maximus Gorog f239a939ce Add Docker + Caddy deploy for voxel.mxvs.art
Multi-stage Dockerfile compiles wasm client + axum server in one Rust
builder and copies into a debian:bookworm-slim runtime (non-root uid).
docker-compose.yml binds localhost:8080 by default; docker-compose.prod.yml
replaces ports with a Caddy reverse proxy on host 80/443 that talks to
the voxel container over the internal network. Caddy auto-issues Let's
Encrypt certs.

DEPLOY.md covers the three deployment modes (local-only, VPS with
Cloudflare or Caddy, Cloudflare Tunnel from a workstation).
2026-05-23 18:45:05 -06:00

3.1 KiB
Raw Blame History

Deploying the voxel game

Three layers, pick the combination that fits.

Local-only (development)

docker compose up --build

Serves on http://localhost:8080. 127.0.0.1-bound — not reachable from the network. Good for iterating on Docker without exposing anything.

Anywhere with a public IP (the real deploy)

This assumes a cheap VPS with Docker + docker-compose-plugin installed. Tested targets: Hetzner CPX11 ($5/mo), DigitalOcean Basic Droplet ($4), Vultr ($2.50), Oracle Cloud Always Free tier (ARM Ampere instance — free forever, just slow to provision).

1. SSH in and install Docker

Debian/Ubuntu host:

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER     # log out + back in

2. Get the code on the box

git clone https://maxgit.wg/max/terainia.git  # or wherever you push it
cd terainia

3. Build + run

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build

The container binds to 0.0.0.0:8080. The first build takes ~58 min on a small VPS because it compiles Rust + the wasm client; subsequent builds are fast due to layer caching.

4. Put TLS in front

You have two clean options here.

Option A — Cloudflare proxy (no cert on the VPS, simplest).

  • In your Cloudflare dashboard for mxvs.art, add an A record: voxel<VPS public IP>, proxy status Proxied (orange cloud).
  • Cloudflare → SSL/TLS → set encryption mode to Flexible (Cloudflare terminates HTTPS, talks HTTP to the VPS).
  • That's it. https://voxel.mxvs.art serves the game.

Option B — Caddy on the VPS for Let's Encrypt.

If you want real end-to-end TLS instead of Cloudflare-terminated:

# docker-compose.prod.yml addition
  caddy:
    image: caddy:2-alpine
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

volumes:
  caddy_data:
  caddy_config:
# Caddyfile
voxel.mxvs.art {
    reverse_proxy voxel:8080
}

Then keep voxel bound to 127.0.0.1:8080 (or use Compose's internal network only — drop the ports: mapping on the voxel service and let Caddy reach it via the service name). The DNS A record in Cloudflare should be DNS only (grey cloud) in this case so Cloudflare doesn't re-terminate TLS.

5. Deploy updates

git pull
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build

Down-and-up time is a couple of seconds while the new container swaps in.

Local + Cloudflare Tunnel (no VPS)

If you don't want a VPS yet, the game can run on your machine and be exposed through a Cloudflare named tunnel pointing at voxel.mxvs.art. The Dockerfile is still useful for keeping the local environment consistent — just docker compose up and then point a cloudflared container at host.docker.internal:8080. See the Cloudflare Tunnel docs for the named-tunnel setup; the credential file (cert.pem from cloudflared tunnel login) needs to land at ~/.cloudflared/cert.pem on whichever host the tunnel runs on.