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).
3.1 KiB
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 ~5–8 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.artserves 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.