CloudAtHome lets you run application servers at home and reach them from the internet through a cloud relay — without opening any inbound firewall port on the home network, and without handing the relay operator anything that would let them read your traffic.
This repo is the cloud-side component: HAProxy plus the Django API/SSH server that homes connect to. It's meant to be deployed by whoever is providing the relay — that might be you, running it purely for your own homes, or for a community or family that registers against your instance. This README covers operating that cloud server. If you're looking to connect a home rather than operate a cloud server, you want the other repo instead: the home-side client (the cah.py CLI + Home Console Django app that runs at a home) lives at otsakir/cloudathome-client.
- Vision — why the cloud is deliberately kept "dumb", and what that buys a home operator
- Architecture — components and the full request/tunnel lifecycle
- REST API reference — the contract home-side clients talk to
- Home-configurable features — base domains, bandwidth throttling, custom inbound ports: home-operator self-service, but useful when triaging a report
- Testing your deployment locally — an end-to-end smoke test on one machine, no real domain needed
- Roadmap — potential future directions
To deploy and run the cloud server for the first time, follow the four steps below:
cp .env.example .envBy default, both Django (website/API) and homes' public endpoints listen on
ports 80/443. Tweak CAH_HTTP_PORT/CAH_HTTPS_PORT or HTTP_INBOUND_DEFAULT_PORT/
HTTPS_INBOUND_DEFAULT_PORT. This is also how you run more than one CloudAtHome instance
on the same host — see
Running more than one instance on the same host
for more on this.
Two more things matter before you're actually done:
- Set
CAH_HOSTNAMEin.envto a real hostname you control — this is the only way to reach Django (admin/API/web UI);tunnelagentrefuses to start at all without it. - Drop a TLS cert/key pair at
docker/django/certs/fullchain.pemanddocker/django/certs/privkey.pem— gunicorn terminatesCAH_HOSTNAME's HTTPS with these (no ACME automation; see docker/django/certs/README.md for where to get one, including a self-signed option for local testing). UnlikeCAH_HOSTNAME, this one doesn't block startup — without it, Django stays reachable over plain HTTP, just without HTTPS.
See Routing Django's admin/API through HAProxy for the full picture.
Next, configure the instance capacity. The number of homes allowed to register and the number
of proxied ports per home (MAX_HOME_COUNT, PORTS_PER_HOME, and friends; defaults to 10
homes). This is an install-time-only decision — there's no supported way to
change it once homes have registered — so decide it now if the defaults don't
fit, then lock it in:
python3 scripts/generate_fleet_config.pyThis validates those settings, writes the per-home backend definitions into
docker/haproxy/haproxy.cfg, derives TCP_PUBLIC_PORT_RANGE in .env from them,
and writes docker/django/fleet_config.json, which the build below bakes into the
tunnelagent image — the build fails if this hasn't been run first.
In case you have already initialized the instance, you will need to reset it by removing src/var/db.sqlite3 first.
docker compose -f compose.yaml up --buildThis starts two containers:
- haproxy — listens on ports 80 and 443 (HTTP/HTTPS), the alternate HTTP/HTTPS range from
.env, and 10000–10099 (TCP forwards) - tunnelagent — Django (reachable only through HAProxy, at
CAH_HOSTNAME— see below), SSH server on port 8022
HAProxy must pass its health check before tunnelagent starts.
docker compose -f compose.yaml exec tunnelagent python /opt/app/manage.py migrate
docker compose -f compose.yaml exec tunnelagent python /opt/app/manage.py createsuperuserThe migrate step also provisions this instance's home slots automatically via the data migration tunnels/migrations/0003_provision_homes.py, sized to whatever MAX_HOME_COUNT was locked in at step 2 (10 by default) — fixed for the life of this instance, per the fleet-size note above.
The SQLite database is stored outside the container at src/var/db.sqlite3.
Steps 1, 2, and 4 only apply to a fresh checkout — restarting an existing instance is just docker compose -f compose.yaml up.
Once running (substituting your CAH_HOSTNAME):
Swagger UI: http://<CAH_HOSTNAME>/api/schema/swagger/
Django admin: http://<CAH_HOSTNAME>/admin/login/
Want to confirm it all actually works before pointing a real home at it? See Testing your deployment locally.
Anyone can self-register at http://<CAH_HOSTNAME>/signup/, but new accounts are created inactive — as the administrator, you're the one who unlocks them:
- Go to the Django admin at
http://<CAH_HOSTNAME>/admin/. - Open the new user, tick Active, and save.
That's the entire admin-side involvement in onboarding. From here the home operator logs into their own dashboard, generates their own API token, and takes it to their own machine to run cah.py register — all of that happens on their side, in cloudathome-client, not yours.
Once activated, a home operator's own dashboard (/dashboard/) shows their home's connection details and a read-only list of currently live proxy mappings — creating and removing mappings is a Home Console (home-side) responsibility, not something exposed here. From their dashboard a user can also generate/rotate their own API token, update their registered SSH public key, download a home-side config.yaml template (with the auth token and key path left blank, so it's safe to view without leaking a live credential), and release their own home slot.
Two endpoints exist specifically for you as the operator, not for home users (full details in the API reference):
GET /api/admin/proxy-mappings/haproxy— dump every live HAProxy map entry, useful for debugging routing without shelling into the container.POST /api/admin/homes/sync— re-derive system SSH users from the database on demand (the same reconciliation that already runs automatically on container startup viareconcile_tunnel_users/reconcile_bandwidth).
The home-side CLI (cah.py) and Home Console (the Django app homes run locally to manage their forwards, certificates, and tunnels) live in a separate repo: otsakir/cloudathome-client. Point your users there for registering a home, managing proxy entries and base domains, obtaining TLS certificates, and managing tunnels.