An ownscale deployment is two containers: ownscale and caddy.
flowchart TB
Internet((Internet)) --> Caddy
subgraph host
Caddy[caddy: TLS, routing, docs site]
subgraph ownscale process
Console[console + API]
OIDC[OIDC provider]
HS1[Headscale: org abcd]
HS2[Headscale: org efgh]
Funnel[funnel proxy]
Backup[Litestream]
end
DB[(SQLite databases)]
end
Caddy -->|console.BASE_DOMAIN| Console
Caddy -->|abcd.BASE_DOMAIN| HS1
Caddy -->|efgh.BASE_DOMAIN| HS2
Caddy -->|label-abcd.CONTENT_DOMAIN| Funnel
ownscale process --- DB
Backup --> Remote[(S3 / Azure / file)]
Caddy
Caddy terminates TLS for every hostname with wildcard certificates obtained through a DNS-01 challenge, and routes by hostname:
<BASE_DOMAIN>: this documentation site, built into the Caddy image;console.<BASE_DOMAIN>: the web console and its API;<org>.<BASE_DOMAIN>: the organization’s Headscale instance;<label>-<org>.<CONTENT_DOMAIN>: a funnel.
See Domains and URLs.
One process, one Headscale per organization
Every organization’s Headscale runs inside the ownscale process, from a fork
at github.com/ownscalenet/headscale.
Each instance listens on a loopback port that ownscale forwards
<org>.<BASE_DOMAIN> traffic to, and ownscale manages it through a unix
socket, so no Headscale API keys exist. Running in-process keeps memory and
IO low enough to host many organizations on a small server. Idle instances
are stopped after 30 minutes and restarted on demand.
The DERP map is fetched by ownscale every 3 hours and pushed to every
instance. Headscale’s embedded DERP server isn’t used, because every
instance would need its own public port. Set OWNSCALE_DERP_MAP_URL to use
your own relays.
Login
ownscale is an OpenID Connect provider that every organization’s Headscale trusts. When a Tailscale client signs in, Headscale redirects to ownscale, which signs the user in with the configured upstream OIDC provider, Google, Microsoft, GitHub or a passkey, then sends them back.
Funnels and the agent machine
A funnel needs a machine
inside the tailnet that can reach the target. When an organization’s first
funnel is created, ownscale starts an embedded Tailscale client (tsnet)
that joins the tailnet as the ownscale-agent machine, owned by an
ownscale-agent user. Public requests arrive at Caddy, go to ownscale’s
funnel proxy and are forwarded through that client to the target machine.
Funnels are experimental and will change.
Database and backups
ownscale stores everything in SQLite: one database for ownscale itself
(users, organizations, invites, funnels, settings) and one per organization
for its Headscale instance. All of them live under OWNSCALE_DATA_DIR.
Litestream runs inside the process and continuously
replicates every database to S3, an S3-compatible service, Azure Blob
Storage or a local directory. Replication runs on an interval
(OWNSCALE_BACKUP_SYNC_INTERVAL, 10 s by default), so if the server dies,
the most recent changes may not have been
replicated yet. This
trade-off gives a fast database with no cluster to manage. See
osa restore.