Build & Deploy
Run a dev server, build for production, and deploy to any hosting provider. The What framework runs anywhere Docker or Rust runs.
Development Server
Start the dev server with file watching and auto-reload:
run-what dev
Options:
| Flag | Default | Description |
|---|---|---|
--path, -p | . | Path to project directory |
--port, -P | 8085 | Port to listen on |
--host, -H | 127.0.0.1 | Host to bind to |
--verbose, -v | — | Enable debug logging |
--strict | — | Warn about unresolved template variables |
--open, -o | — | Open browser after server starts |
--production | — | Production mode: disables live reload, debug tags, source viewer |
# Common usage
run-what dev --port 3000 --verbose
run-what dev --path ./my-project --strict
The dev server watches for file changes and reloads automatically. No need to restart when editing templates, components, or static files.
Production Build (Static Sites)
For static sites without server-side features, pre-render to HTML:
run-what build --output dist
This renders all pages to static HTML, minifies CSS/JS, and copies assets. Deploy the dist/ folder to any static host (Cloudflare Pages, Netlify, Vercel).
Docker (Any Host)
Docker is the simplest way to deploy What to any server. One command generates an optimized Dockerfile.
Step 1: Generate Dockerfile
run-what deploy --target docker
This creates an optimized multi-stage Dockerfile that builds your binary and creates a minimal runtime image.
Step 2: Build and Run
# Build the image
docker build -t my-app .
# Run with data persistence
docker run -d \
--name my-app \
-p 8085:8085 \
-v $(pwd)/data:/app/project/data \
-v $(pwd)/uploads:/app/project/uploads \
my-app
-v volume mounts are essential. Without them, your database and uploads are destroyed when the container restarts. Always mount data/ and uploads/. For production data that must persist reliably, consider using Cloudflare D1 or Supabase instead of local SQLite.
What Persists Across Container Restarts
| File | Persists? | Requires |
|---|---|---|
data/app.db (database) | Yes | -v ./data:/app/project/data |
data/sessions.db | Yes | Same volume as above |
uploads/ | Yes | -v ./uploads:/app/project/uploads |
| Templates, components | Rebuilt on deploy | Baked into image |
Hetzner VPS (SSH + Docker)
Hetzner offers affordable VPS hosting starting at ~4.50/month. This is how getwhatnow.com is deployed.
Prerequisites
- A Hetzner Cloud account
- An SSH key added to your Hetzner account
Step 1: Create a Server
- Go to Hetzner Cloud Console
- Click Add Server
- Choose Ubuntu 24.04, CX22 (2 vCPU, 4 GB RAM)
- Add your SSH key
- Click Create & Buy Now
- Note the server IP address
Step 2: Install Docker on the Server
ssh root@YOUR_SERVER_IP "curl -fsSL https://get.docker.com | sh"
Step 3: Deploy with run-what
run-what deploy --target ssh
The first run prompts for your server details interactively:
- Host: Your server IP (e.g.,
65.108.xx.xx) - User:
root - Remote directory:
/opt/what(default)
This builds a release binary, syncs your project via rsync, and installs a systemd service for automatic restarts. Config is saved to what.toml for future deploys:
[deploy.ssh]
host = "65.108.xx.xx"
user = "root"
remote_dir = "/opt/what"
Subsequent deploys are one command: run-what deploy --target ssh
Data Persistence
The SSH deploy uses rsync which excludes data/ and *.db files from sync. Your database and sessions remain untouched on the server across redeploys.
Digital Ocean App Platform
Deploy from a GitHub repository with automatic builds. Digital Ocean's App Platform detects the Dockerfile and handles the rest.
Prerequisites
- A Digital Ocean account
- Your project pushed to GitHub
Step 1: Generate the Dockerfile
run-what deploy --target docker
git add Dockerfile && git commit -m "Add Dockerfile" && git push
Step 2: Create the App
- Go to Digital Ocean Apps
- Click Create App, connect your GitHub repo
- Select the branch and Dockerfile
- Add a persistent volume:
- Mount path:
/app/project/data - Size: 1 GB (can resize later)
- Mount path:
- Set the HTTP port to
8085 - Deploy
Environment Variables
Add any ${ENV_VAR} values (database credentials, API keys) in the App Platform settings under App-Level Environment Variables.
Data Persistence
The persistent volume at /app/project/data survives all redeploys and container restarts. Your SQLite database and sessions are safe.
Cloudflare with D1 + R2
For global edge performance, deploy your What server on any VPS and use Cloudflare services for data and uploads. This is not a static deployment — your server runs What and connects to Cloudflare for persistent storage.
Architecture
[Your VPS] ——> [Cloudflare D1] (database)
|
+——> [Cloudflare R2] (file uploads)
|
+——> [Local SQLite] (sessions only)
Step 1: Set Up D1 and R2
Follow the D1 setup instructions in the Database section above. For uploads, create an R2 bucket in the Cloudflare dashboard.
Step 2: Configure what.toml
[database]
type = "d1"
[cloudflare]
account_id = "${CF_ACCOUNT_ID}"
api_token = "${CF_API_TOKEN}"
d1_database_id = "your-d1-database-id"
r2_bucket = "my-uploads"
r2_public_url = "https://pub-abc123.r2.dev"
[uploads]
enabled = true
provider = "r2"
Step 3: Deploy to Any VPS
Use any of the deployment methods above (Hetzner SSH, Docker, Digital Ocean). The server connects to Cloudflare for data, so your database persists regardless of what happens to the VPS.
Data Persistence
- Database: Lives in D1 (cloud) — always persists
- Uploads: Lives in R2 (cloud) — always persists
- Sessions: Local SQLite on the VPS — survives redeploys if
data/is preserved
Docker Compose (Production Setup)
For a production setup with SSL and reverse proxy, use Docker Compose:
services:
app:
build: .
restart: unless-stopped
volumes:
- ./my-project:/app/project:ro
- app-data:/app/project/data
- app-uploads:/app/project/uploads
environment:
- RUST_LOG=info
command: ["run-what", "dev", "--path", "/app/project", "--host", "0.0.0.0", "--production"]
nginx:
image: nginx:alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certbot/conf:/etc/letsencrypt:ro
- ./certbot/www:/var/www/certbot:ro
depends_on:
- app
certbot:
image: certbot/certbot
volumes:
- ./certbot/www:/var/www/certbot
- ./certbot/conf:/etc/letsencrypt
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
volumes:
app-data:
app-uploads:
Named volumes (app-data, app-uploads) persist across container rebuilds.
Standalone Executable
Package your What app as a standalone binary that anyone can run — no Rust or What installation required. The binary embeds all project files, starts the server, and opens a browser automatically.
Generate the Bundle
run-what bundle
This creates a bundle/ directory containing a complete Rust project:
project.tar.gz— compressed archive of your site filesCargo.toml— depends onwhat-corefrom crates.iosrc/main.rs— extracts files, starts server, opens browser
Compile to a Binary
# Option 1: Compile separately
cd bundle && cargo build --release
# Binary at bundle/target/release/what-app
# Option 2: Compile in one step
run-what bundle --compile
# Binary at bundle/what-app
Options:
| Flag | Default | Description |
|---|---|---|
--path, -p | . | Path to project directory |
--output, -o | bundle | Output directory for the generated project |
--compile | — | Also compile the bundle to a release binary |
Running the Binary
# Just run it — browser opens automatically
./what-app
The binary extracts project files to a temp directory and starts the server. Database and uploads are stored in the current working directory:
./data/— SQLite database and sessions./uploads/— uploaded files
run-what dev instead.
cargo build --target x86_64-unknown-linux-gnu inside the bundle directory. Share the resulting binary with anyone — they just double-click to run.
Sitemap Generation
Generate a sitemap.xml from your project's routes:
run-what sitemap --host https://example.com
| Flag | Default | Description |
|---|---|---|
--path, -p | . | Path to project directory |
--host | — | Base URL for the sitemap (required) |
--output, -o | static/sitemap.xml | Output file path |
Health Check
Verify your project setup before deploying:
run-what doctor
Checks project structure, template syntax, component resolution, and configuration correctness.