what

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:

FlagDefaultDescription
--path, -p.Path to project directory
--port, -P8085Port to listen on
--host, -H127.0.0.1Host to bind to
--verbose, -vEnable debug logging
--strictWarn about unresolved template variables
--open, -oOpen browser after server starts
--productionProduction 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).

Note: Static builds only work for pages that don't use server-side features (sessions, form actions, uploads, database). For dynamic features, deploy as a running server using one of the methods below.

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
Critical: The -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

FilePersists?Requires
data/app.db (database)Yes-v ./data:/app/project/data
data/sessions.dbYesSame volume as above
uploads/Yes-v ./uploads:/app/project/uploads
Templates, componentsRebuilt on deployBaked 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

  1. Go to Hetzner Cloud Console
  2. Click Add Server
  3. Choose Ubuntu 24.04, CX22 (2 vCPU, 4 GB RAM)
  4. Add your SSH key
  5. Click Create & Buy Now
  6. 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:

what.toml
[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.

Cost: Hetzner CX22 costs ~4.50/month with 40 GB disk, 20 TB traffic. Plenty for most What apps.

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

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

  1. Go to Digital Ocean Apps
  2. Click Create App, connect your GitHub repo
  3. Select the branch and Dockerfile
  4. Add a persistent volume:
    • Mount path: /app/project/data
    • Size: 1 GB (can resize later)
  5. Set the HTTP port to 8085
  6. 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.

Cost: Digital Ocean App Platform starts at ~$5/month. Add $1/month for 1 GB persistent storage.

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

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
Tip: Cloudflare D1 and R2 both have generous free tiers. This architecture gives you cloud-hosted data with the simplicity of a single VPS.

Docker Compose (Production Setup)

For a production setup with SSL and reverse proxy, use Docker Compose:

docker-compose.yml
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 files
  • Cargo.toml — depends on what-core from crates.io
  • src/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:

FlagDefaultDescription
--path, -p.Path to project directory
--output, -obundleOutput directory for the generated project
--compileAlso 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
Note: The standalone binary does not include file watching or live reload. It runs in production mode. To develop your app, use run-what dev instead.
Tip: Cross-compile for other platforms using 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
FlagDefaultDescription
--path, -p.Path to project directory
--hostBase URL for the sitemap (required)
--output, -ostatic/sitemap.xmlOutput file path

Health Check

Verify your project setup before deploying:

run-what doctor

Checks project structure, template syntax, component resolution, and configuration correctness.