osmRouterosmRouter

Self-hosting

Run the whole osmRouter stack on a single box you control with Docker Compose.

The entire platform — relay, control-plane API, Postgres, Redis, and Caddy for TLS — ships as a Docker Compose deployment. Run it on one server and the hosted limits no longer apply; you set your own.

What you'll run

ServiceRole
relayTunnel data plane — agents dial in, visitors are forwarded out.
apiControl plane — auth, tokens, domains, usage, metering.
caddyTLS termination (Let's Encrypt), wildcard certs, serves the web apps.
postgresAccounts, tunnels, domains, usage.
redisEphemeral state and coordination.

Prerequisites

  • A server with a public IP (any small cloud VM works).
  • A domain you control, with DNS you can edit.
  • Docker and Docker Compose installed.

DNS

Point these records at your server's IP — the wildcard is required for random tunnel URLs:

RecordTypePoints to
@Aserver IP
app, api, docs, statusAserver IP
tunnelAserver IP
*Aserver IP (wildcard — required for <name>.yourdomain.com)

Without the * wildcard record and wildcard TLS, random and pinned tunnel subdomains can't resolve or get a certificate. This is the one record people miss.

Bring it up

Get the self-host bundle

The full platform (relay, control plane, Postgres, Redis, Caddy) ships as a Docker Compose bundle, available with the Enterprise / self-host plan. Email contact@osmapi.com to get it, then configure your domain and secrets:

cp .env.example .env
# set OSM_DOMAIN=yourdomain.com and the generated secrets

Start the stack

docker compose --env-file .env -f infra/docker-compose.yml up -d

The API applies database migrations on boot.

Verify

Check the services are healthy and the public URLs answer:

docker compose -f infra/docker-compose.yml ps
curl -I https://app.yourdomain.com

Caddy issues certificates on first request; if issuance fails, confirm DNS has fully propagated and restart Caddy.

Point the client at your relay

Your users set OSM_RELAY and OSM_DOMAIN to your deployment:

OSM_RELAY=tunnel.yourdomain.com:8443 \
OSM_DOMAIN=yourdomain.com \
OSM_TOKEN=osm_xxxxx \
osmrouter http 8080

Operating it

  • Logs: docker compose -f infra/docker-compose.yml logs -f api
  • Rebuild after an update: git pull && docker compose -f infra/docker-compose.yml up -d --build
  • Backups: back up the Postgres volume regularly.

Next steps