funnel

Deployment

Run the funnel server

The server is a single binary that handles HTTP API, QUIC tunnel connections, and optionally TLS termination.

Minimal startup

With the embedded Turso database (no external dependencies):

funnel-server --seed-api-key

This starts the server on port 8080 (HTTP) and 4433 (QUIC), creates an embedded database at ./funnel.db, and prints a seed API key to stdout.

With PostgreSQL

funnel-server \
  --database-url postgres://user:pass@localhost/funnel \
  --seed-api-key

Migrations run automatically on startup. The server verifies schema version compatibility before accepting connections.

Configuration

All options can be set via CLI flags or environment variables.

FlagEnvDefaultDescription
--hostFUNNEL_HOST0.0.0.0Bind address
--portFUNNEL_PORT8080HTTP port
--quic-portFUNNEL_QUIC_PORT4433QUIC port for tunnel connections
--database-urlDATABASE_URL-PostgreSQL connection URL
--db-max-connectionsFUNNEL_DB_MAX_CONNECTIONS10Connection pool size
--turso-db-pathFUNNEL_TURSO_DB_PATH./funnel.dbEmbedded database path
--seed-api-keyFUNNEL_SEED_API_KEYfalseCreate a seed API key on startup
--initial-admin-emailFUNNEL_INITIAL_ADMIN_EMAIL-Auto-promote this email to admin on first login
--enable-tcp-tunnelsFUNNEL_ENABLE_TCP_TUNNELSfalseEnable TCP/TLS tunnel support
--stream-port-minFUNNEL_STREAM_PORT_MIN10000Lowest port for TCP tunnel allocation
--stream-port-maxFUNNEL_STREAM_PORT_MAX60000Highest port for TCP tunnel allocation

For TLS and OAuth options, see TLS and authentication.

TCP tunnels

TCP tunnels are disabled by default because they allocate ports directly on the server. To enable them:

funnel-server --enable-tcp-tunnels

When enabled, tunnels allocate ports from the stream-port-min to stream-port-max range. Make sure these ports are open in your firewall:

funnel-server --enable-tcp-tunnels --stream-port-min 20000 --stream-port-max 20100
sudo ufw allow 20000:20100/tcp

Without --enable-tcp-tunnels, clients requesting TCP or TLS tunnels receive an unsupported_tunnel_type error.

Database choice

PostgreSQL is the production choice. It supports concurrent access, has proper ACID guarantees, and scales with connection pooling.

Embedded Turso (libsql) is useful for development, single-user setups, or environments where running PostgreSQL is impractical. Data is stored in a single file. No external process needed.

If --database-url is set, PostgreSQL is used. Otherwise the server falls back to the embedded database at --turso-db-path.

Docker

OCI images are published for linux/amd64 and linux/arm64:

docker run \
  -p 8080:8080 \
  -p 4433:4433/udp \
  -p 10000-60000:10000-60000 \
  ghcr.io/karol-broda/funnel-server

The port range 10000-60000 is for TCP tunnels. If you don't need TCP tunnels, omit it. To use a smaller range:

docker run \
  -p 8080:8080 \
  -p 4433:4433/udp \
  -p 20000-20100:20000-20100 \
  -e FUNNEL_STREAM_PORT_MIN=20000 \
  -e FUNNEL_STREAM_PORT_MAX=20100 \
  ghcr.io/karol-broda/funnel-server

NixOS

For declarative NixOS deployments with systemd hardening, see the NixOS module.

On this page