Networking

Temps runs a built-in reverse proxy (Caddy) that handles TLS termination, routing, and port management for all your deployments. This page covers how to configure ports, internal networking between services, firewall rules, and advanced proxy settings.


Ports

Each deployed container exposes one primary port. Temps auto-detects it from your Dockerfile EXPOSE directive or framework defaults, or you can set it explicitly.

Auto-detected defaults by framework:

FrameworkDefault Port
Next.js / Node.js3000
Python (uvicorn)8000
Go8080
Ruby on Rails3000
Custom DockerFrom EXPOSE

To override, set the PORT environment variable in Project → Settings → Environment Variables.

Your app must bind to 0.0.0.0, not 127.0.0.1, or the proxy cannot reach it.


Internal Networking

Services within the same Temps instance communicate over a private Docker network without exposing ports to the internet. Use the container's service name as the hostname.

If you have a project named api deployed alongside postgres:

postgresql://postgres:password@postgres:5432/mydb

Internal hostnames follow the pattern <service-name>.<project-name>.internal for cross-project references:

http://api.my-project.internal:3000

Internal DNS resolver

Each node runs a built-in DNS resolver (the temps-dns-resolver crate, a Hickory-based UDP/TCP server bound on the bridge gateway). Containers use it as their first nameserver, which makes the *.temps.local names that back HA database clusters resolve natively from inside any cluster member.

The resolver decides how to answer each query by where the name falls:

QueryHow it is answered
In the temps.local zone (temps.local or any *.temps.local, matched case-insensitively, trailing dot optional)Answered authoritatively from the node's synced zone snapshot.
In-zone name that does not existNXDOMAIN.
In-zone name that exists but not for the requested record typeNODATA (NoError with an empty answer) — so IPv4-only names don't break AAAA lookups in glibc/busybox.
Outside temps.localRecursively forwarded to the upstream public resolvers (see below).

Recursive forwarding for public domains

Because the internal resolver is the only nameserver your containers see, queries for public domains (package mirrors, third-party APIs, and so on) are recursively forwarded to a pool of upstream public resolvers. Without this, every outbound lookup like apt-get, wget, or an external API call would return NXDOMAIN.

The default upstream pool is hardcoded:

UpstreamAddressPort
Cloudflare1.1.1.153
Cloudflare1.0.0.153
Google8.8.8.853

Forwarded answers come back as non-authoritative with recursion-available set. An upstream NXDOMAIN/NODATA is passed through as a negative answer; a transport failure (timeout, unreachable upstream) is returned as SERVFAIL so the client can retry.

If the upstream list is configured empty, the forwarder is disabled — the resolver logs DNS recursive forwarder disabled (empty upstream list) instead of DNS recursive forwarder enabled, and out-of-zone queries fall through to NXDOMAIN (strict authoritative-only behavior).


Firewall Rules

Temps manages ufw firewall rules automatically. By default:

  • Port 80 (HTTP) — open, redirects to HTTPS
  • Port 443 (HTTPS) — open
  • Port 22 (SSH) — open (for server access)
  • All other ports — closed

To expose an additional port (e.g. for a custom TCP service):

# On the server
sudo ufw allow 25565/tcp comment "custom-service"

For database ports, keep them closed to the internet and use SSH tunneling for local access:

ssh -L 5432:localhost:16432 user@your-server-ip
# Then connect to localhost:5432 locally

Reverse Proxy Configuration

The built-in Caddy proxy handles all routing. For most apps no configuration is needed. For advanced cases, add a Caddyfile snippet via Project → Settings → Proxy Config:

# Increase request body size limit (default 10MB)
request_body {
  max_size 100MB
}

# Add custom response headers
header {
  X-Frame-Options DENY
  Strict-Transport-Security "max-age=31536000; includeSubDomains"
}

WebSockets

WebSocket connections are proxied automatically — no additional configuration required. Ensure your app handles the Upgrade and Connection headers, which Caddy forwards as-is.

gRPC

For gRPC services, set the project type to gRPC in Project → Settings → General. Caddy switches to HTTP/2 transport automatically.


Load Balancing

When you scale a project to multiple replicas, Temps load-balances incoming requests across them using round-robin with health-check exclusion. Unhealthy replicas are removed from rotation within 10 seconds of a failed health check.

Configure the health check endpoint under Project → Settings → Health Check:

Path:      /health
Interval:  10s
Timeout:   5s
Threshold: 2 failures

Your app should return 200 OK from that path. Any non-2xx response marks the replica unhealthy.


Private Networking (Tailscale)

For multi-server setups or connecting your laptop directly to Temps services, Tailscale is the recommended approach:

# On the Temps server
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Services become reachable at their Tailscale IP (100.x.x.x) without any firewall changes. This is the safest way to expose database ports to developers without opening them to the internet.


Next Steps

Was this page helpful?