Configuration Reference

Server configuration via environment variables in .env

All configuration is via environment variables. The server reads them at startup from the .env file in the Docker Compose directory. After changing .env, restart the server:

bash
docker compose up -d tunnl

Required

The server will not start without these two variables.

VariableDescriptionExample
DOMAIN Base domain for the server. Tunnel URLs are <subdomain>.<DOMAIN>. Do not include https://. Must match your DNS wildcard record. your.domain.com
JWT_SECRET Secret key for signing authentication tokens. Must be at least 32 characters. Treat as a password — do not share or commit. Generate with openssl rand -hex 32. a3f8... (64 hex chars)

Database

VariableDescriptionDefault
DATABASE_URL PostgreSQL connection string. In the Docker Compose setup, this is set automatically to the bundled PostgreSQL container. Override to point at an external database. Use sslmode=require for remote databases. postgres://tunnl:tunnl@postgres:5432/tunnl?sslmode=disable
📋
If you use an external PostgreSQL instance, the database user needs CREATE TABLE and CREATE INDEX privileges. Migrations run automatically on first startup.

Server Behavior

VariableDescriptionDefault
PORT Port the Go server listens on inside Docker. Caddy reverse-proxies to this port. 8080
MAX_TUNNELS Maximum concurrent tunnel connections per user account. 5
REQUESTS_PER_HOUR Rate limit per tunnel, sliding window. Requests above this limit return 429 Too Many Requests to the caller. 10000
FREE_SESSION_TIMEOUT Session timeout in seconds for free-tier users. Set to 0 to disable. Default allows sessions to persist. 3600
TRUSTED_PROXIES Comma-separated list of trusted proxy CIDRs for real IP extraction. Set to your load balancer or Caddy IP. Defaults to loopback only. 127.0.0.1

TLS (Caddy)

TLS is handled entirely by Caddy, which runs on the host separately from Docker Compose. There are no TLS-related environment variables in the tunnl-server itself. Configure TLS certificate paths directly in your Caddyfile:

Caddyfile
your.domain.com, *.your.domain.com {
    tls /etc/ssl/tunnl/fullchain.pem /etc/ssl/tunnl/privkey.pem
    reverse_proxy localhost:8080
}

See the Install Guide TLS section for full setup instructions including certificate renewal.

Email (SMTP)

Email is used for account verification and password reset. If not configured, the server logs verification tokens to stdout — usable for local development but not for multi-user deployments.

VariableDescriptionExample
SMTP_HOST SMTP server hostname. smtp.resend.com
SMTP_PORT SMTP port. Use 587 for STARTTLS (recommended), 465 for TLS, 25 for unencrypted. 587
SMTP_USERNAME SMTP username for authentication. resend
SMTP_PASSWORD SMTP password or API key. Keep this secret. re_...
SMTP_FROM From address for outgoing email. Must be a verified sender domain with your SMTP provider. noreply@your.domain.com
💡
Recommended SMTP providers: Resend (generous free tier), AWS SES, Postmark. Avoid using Gmail SMTP for production — rate limits are tight and deliverability suffers.

Redis (Optional)

Redis is not required. Without it, rate limiting is in-memory and resets on restart. Configure Redis if you plan to run multiple server instances.

VariableDescriptionExample
REDIS_URL Redis connection string. Leave blank to use in-memory rate limiting. When set, the rate limiter state persists across restarts and is shared between multiple server instances. redis://localhost:6379

Example .env

A complete .env for a production single-server deployment:

.env
# Required
DOMAIN=your.domain.com
JWT_SECRET=<output of: openssl rand -hex 32>
DB_PASSWORD=<secure-password>
SELF_HOSTED_MODE=true

# Server behavior (optional)
PORT=8080
MAX_TUNNELS=25
REQUESTS_PER_HOUR=50000
TRUSTED_PROXIES=127.0.0.1
FREE_SESSION_TIMEOUT=3600

# Email (optional but recommended for multi-user)
SMTP_HOST=smtp.resend.com
SMTP_PORT=587
SMTP_USERNAME=resend
SMTP_PASSWORD=re_<your_resend_api_key>
SMTP_FROM=noreply@your.domain.com

# Redis (optional, for distributed rate limiting)
REDIS_URL=