Skip to main content
Hatch includes a built-in reverse proxy that routes HTTP traffic from subdomains to VMs. This enables clean URLs like https://my-app.hatch.example.com instead of IP:port combinations.

How the Reverse Proxy Works

The proxy uses subdomain-based routing:
1

Client makes request

A request arrives at https://my-app.hatch.example.com
2

Proxy extracts subdomain

The proxy extracts my-app from the Host header.
3

Route lookup

The proxy queries the database for a route matching subdomain my-app.
4

VM state check

If the target VM is snapshotted and auto_wake: true, the VM is automatically restored.
5

Request forwarded

Once the VM is running, the request is proxied to http://{guest_ip}:{target_port}.

Creating a Proxy Route

Create a route for a running VM:
curl -X POST https://api.hatch.example.com/vms/vm-abc123/routes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subdomain": "my-app",
    "target_port": 8080
  }'
Response:
{
  "id": "rt-xyz789",
  "vm_id": "vm-abc123",
  "subdomain": "my-app",
  "target_port": 8080,
  "auto_wake": true,
  "created_at": "2026-03-06T14:30:00Z",
  "updated_at": "2026-03-06T14:30:00Z"
}
Now HTTP requests to https://my-app.hatch.example.com will be forwarded to the VM’s port 8080.
If you omit the subdomain field, Hatch generates a random 12-character subdomain automatically.

Subdomain Patterns

Valid Subdomains

Subdomains must follow these rules:
  • Lowercase alphanumeric characters and hyphens
  • Must start and end with alphanumeric (not hyphen)
  • Maximum 63 characters
  • Cannot be a reserved subdomain
"my-app"
"prod-api"
"web-1"
"staging"
"app-2024-v2"

Reserved Subdomains

These subdomains are reserved and cannot be used:
api, www, admin, app, dashboard, mail, smtp, ftp, ssh,
ns1, ns2, status, docs, help, support, billing, login,
auth, oauth, cdn, static, assets, media, hatch, console,
panel, grafana, prometheus, traefik, minio, postgres,
db, redis, internal
Error when using reserved subdomain:
{
  "error": "subdomain \"api\" is reserved"
}

Random Subdomains

Omit the subdomain field to get an auto-generated unique subdomain:
curl -X POST https://api.hatch.example.com/vms/vm-abc123/routes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "target_port": 3000
  }'
Response with random subdomain:
{
  "id": "rt-xyz789",
  "subdomain": "x7k9m2p5n8q1",
  "target_port": 3000,
  "auto_wake": true
}

Auto-Wake Configuration

The auto_wake field controls whether snapshotted VMs are automatically restored on incoming requests.

Enabled (default)

curl -X POST https://api.hatch.example.com/vms/vm-abc123/routes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subdomain": "my-app",
    "target_port": 8080,
    "auto_wake": true
  }'
Behavior:
  1. Request arrives for my-app.hatch.example.com
  2. VM is in snapshotted state
  3. Proxy automatically calls POST /vms/vm-abc123/restore
  4. VM transitions to running (2-10 seconds)
  5. Request is forwarded to the VM
The first request after a snapshot will have higher latency (restore time). Subsequent requests are instant. The proxy serializes concurrent restore requests to avoid redundant operations.

Disabled

curl -X POST https://api.hatch.example.com/vms/vm-abc123/routes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subdomain": "critical-app",
    "target_port": 8080,
    "auto_wake": false
  }'
Behavior:
  1. Request arrives for critical-app.hatch.example.com
  2. VM is in snapshotted state
  3. Proxy returns 503 Service Unavailable immediately
  4. VM remains snapshotted
Response to client:
{
  "error": "vm is snapshotted and auto-wake is disabled"
}
Disable auto-wake for production VMs that must always be running. Use idle management carefully to avoid unwanted snapshots.

Multiple Routes Per VM

A single VM can have multiple proxy routes for different services:
# Route 1: Web interface
curl -X POST https://api.hatch.example.com/vms/vm-abc123/routes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subdomain": "web",
    "target_port": 80
  }'

# Route 2: API backend
curl -X POST https://api.hatch.example.com/vms/vm-abc123/routes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subdomain": "api",
    "target_port": 8080
  }'

# Route 3: Metrics endpoint
curl -X POST https://api.hatch.example.com/vms/vm-abc123/routes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subdomain": "metrics",
    "target_port": 9090
  }'
All routes share the same VM lifecycle. If the VM is snapshotted, any route with auto_wake: true can trigger a restore.

Listing Routes

All routes for a VM

curl https://api.hatch.example.com/vms/vm-abc123/routes \
  -H "Authorization: Bearer YOUR_API_KEY"
Response:
[
  {
    "id": "rt-xyz789",
    "vm_id": "vm-abc123",
    "subdomain": "my-app",
    "target_port": 8080,
    "auto_wake": true,
    "created_at": "2026-03-06T14:30:00Z"
  },
  {
    "id": "rt-abc456",
    "vm_id": "vm-abc123",
    "subdomain": "my-api",
    "target_port": 3000,
    "auto_wake": false,
    "created_at": "2026-03-06T10:15:00Z"
  }
]

Deleting Routes

Remove a proxy route by its ID:
curl -X DELETE https://api.hatch.example.com/routes/rt-xyz789 \
  -H "Authorization: Bearer YOUR_API_KEY"
Response:
{
  "status": "deleted"
}
Deleting a route does not affect the VM. The VM continues running, but the subdomain will no longer route to it.

Custom Domain Setup

To use your own domain (e.g., *.example.com) instead of the Hatch base domain:
1

Configure DNS wildcard record

Add a wildcard A or CNAME record pointing to your Hatch host:
*.hatch.example.com  A  203.0.113.10
Or using CNAME:
*.hatch.example.com  CNAME  hatch-host.example.com
2

Configure Hatch base domain

Set the HATCH_BASE_DOMAIN environment variable:
export HATCH_BASE_DOMAIN=hatch.example.com
Restart Hatch for the change to take effect.
3

Create routes as normal

Routes will now use your custom domain:
curl -X POST https://api.hatch.example.com/vms/vm-abc123/routes \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "subdomain": "my-app",
    "target_port": 8080
  }'
Access at: https://my-app.hatch.example.com
You’ll also need a reverse proxy (nginx, Caddy, Traefik) in front of Hatch to handle TLS termination. The Hatch proxy speaks HTTP only.

Proxy Behavior

Host Header Preservation

The original Host header is preserved when forwarding requests:
Client request:     Host: my-app.hatch.example.com
Proxied request:    Host: my-app.hatch.example.com
This allows applications to generate correct URLs and handle virtual hosting.

Request Timeout

Requests wait up to 30 seconds for auto-wake to complete (configurable via HATCH_PROXY_WAKE_TIMEOUT). If restore takes longer:
{
  "error": "failed to wake vm"
}

Error Responses

ErrorStatusCause
no subdomain in host header502Host header missing or malformed
no route for subdomain "x"502No route exists for subdomain
vm not found502Route exists but VM was deleted
vm is snapshotted and auto-wake is disabled503VM snapshotted, auto-wake off
failed to wake vm503Restore operation failed
vm is in state "error", not proxying503VM in error state
vm has no guest IP502VM has networking disabled

Idle Tracking

The proxy tracks the last request time for each subdomain. This data is used by the idle monitor to determine when to snapshot inactive VMs. Access tracking:
  • Every successful proxy request updates the last_access timestamp for the subdomain
  • VMs with proxy routes are considered active if they’ve received requests within the idle timeout
  • VMs without recent proxy traffic are candidates for automatic snapshotting
SSH connections are tracked separately via netfilter conntrack, not via proxy access times.

Best Practices

1

Use descriptive subdomain names

Choose subdomains that reflect the service or environment:
prod-api, staging-web, dev-dashboard, qa-frontend
2

Enable auto-wake for dev/staging

Save costs by letting idle dev VMs snapshot automatically:
{
  "subdomain": "dev-app",
  "target_port": 8080,
  "auto_wake": true
}
3

Disable auto-wake for production

Keep production VMs always running:
{
  "subdomain": "prod-api",
  "target_port": 8080,
  "auto_wake": false
}
4

Use multiple routes for complex apps

Route different ports for frontend, backend, and admin panels:
# Frontend
POST /vms/vm-abc123/routes {"subdomain": "app", "target_port": 80}

# API
POST /vms/vm-abc123/routes {"subdomain": "api", "target_port": 8080}

# Admin
POST /vms/vm-abc123/routes {"subdomain": "admin", "target_port": 9000}

Next Steps

Idle Management

Configure automatic snapshots based on proxy traffic

Snapshots

Understand how auto-wake restores VMs

Creating VMs

Learn about VM networking requirements

Network Setup

Configure VMs for HTTP traffic

Build docs developers (and LLMs) love