尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Git push to deploy. On a box you own.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗A single-binary, self-hosted PaaS. Point it at a fresh VPS, push a Git repo, and Outhaul clones it, builds it (Nixpacks), and runs it behind Traefik on a domain you choose — with automatic HTTPS, health-gated deploys, and one-click rollback.
No Node, no Postgres, no control-plane sprawl: one ~22 MB Go binary and a SQLite file. Outhaul is a deliberately minimal alternative to Dokploy and Coolify — the same core workflow, a fraction of the moving parts.
[!WARNING] Outhaul is alpha, pre-release software — not ready for production. Expect breaking changes, missing features, and rough edges, with no upgrade or data-migration guarantees between versions. Only run it on a disposable test server you can afford to wipe — never a production host or a machine holding anything you care about. Outhaul also needs Docker-socket access (which is root-equivalent), so treat any box you install it on as fully entrusted to it.
go build -o outhaul . && ./outhaul serve
# → open the one-time setup URL it prints, create the admin account, deploy.
curl -fsSL https://outhaul.sh/install | sh
The installer walks you through ingress (Let's Encrypt or Cloudflare Tunnel),
opens the right firewall ports (keeping SSH), builds from source, and prints a
one-time setup URL. Developers with a local checkout can run
sudo sh deploy/bootstrap.sh --from-checkout ..
Dockerfile build with it, through the same pipeline.docker-compose.yml deploys as a multi-service stack, with any number of domains routed to its services.cloudflared and routes each Cloudflare public hostname through Traefik. While the tunnel is the ingress, Cloudflare terminates HTTPS at its edge and Outhaul's Let's Encrypt automation stands down.${{project.KEY}}.sslip.io domains — fully editable afterward like any compose app.OUTHAUL_IMAGE_KEEP) while a nightly sweep reclaims old images, dangling layers left by its own builds, and (unless OUTHAUL_PRUNE_BUILD_CACHE=no) stale build cache.See ARCHITECTURE.md for the design and what's intentionally out of scope for now (multiple users, metrics history/alerts, multi-server).
Requirements on the host: a reachable Docker daemon, git, and
nixpacks on PATH. Dockerfile apps additionally
need the docker CLI (docker build), and compose apps the docker CLI
with the compose v2 plugin (docker compose).
go build -o outhaul .
./outhaul serve
On first boot Outhaul prints a one-time setup URL — open it to create the admin
account. The admin UI listens on :8080 by default; Traefik is started
automatically and serves app traffic on :80.
That's fine for kicking the tires; for a server install use the installer below, which sets up a dedicated user and a systemd unit.
[!NOTE] "Server" here means a disposable test server, not production — see the warning at the top. The installer sets up a long-running systemd service, so only point it at a box you're happy to wipe.
Don't run Outhaul as root. On the server:
curl -fsSL https://outhaul.sh/install | sh
That fetches deploy/bootstrap.sh and runs it under
sh. From a checkout, run sudo sh deploy/bootstrap.sh --from-checkout .
instead — useful for testing local changes to the installer or app. Prefer to
run every step by hand? See docs/MANUAL-DEPLOY.md.
The installer needs root once (systemd required; V1 targets Debian and Ubuntu, prompting before continuing on anything else). Each step is skipped when already done, so it is safe to re-run — and a re-run upgrades the binary and restarts the service. It:
outhaul system user (no login shell, home /var/lib/outhaul)
in the docker group;/usr/local/bin/outhaul;/etc/outhaul.env (put OUTHAUL_* overrides there — ACME email,
public URL) and installs and starts outhaul.service
(deploy/outhaul.service);The installer prints the one-time setup URL, and it also lands in the journal:
journalctl -u outhaul | grep -i setup
With Let's Encrypt (or the local CA and a public URL), it is under your public
URL. Otherwise it is on localhost:8080: reach it over an SSH tunnel,
ssh -L 8080:127.0.0.1:8080 you@server.
Why a dedicated user and not root? The process needs exactly the Docker
socket, the git/nixpacks/docker CLIs, and its data dir. It never binds
a privileged port itself — Traefik's container publishes 80/443, and the
(root) Docker daemon does that binding. Note that docker group membership
is still root-equivalent (socket access can mount the host filesystem into a
container), so the dedicated user is least-privilege hygiene rather than a
hard security boundary; the unit layers systemd sandboxing on top
(NoNewPrivileges, PrivateTmp, ProtectSystem=full, ProtectHome).
Settings → Updates compares the server with main and updates it on a click.
Your apps keep running while it builds. Outhaul restarts for a moment, and a
failed update rolls back to the version that was running. Changes to the
installer itself are not part of this: they need an installer run. A server
installed before the Updates panel existed gets it by re-running the installer
once.
An update that changes Traefik's image or configuration restarts Traefik, and every app is unreachable for a few seconds; the panel says so before you click Update.
Each update's log is in /var/lib/outhaul-updater/log on the server
(sudo cat it), and journalctl -u outhaul-update has each update's result.
If an update leaves Outhaul down, the log says how to put the previous
version back.
No config files — defaults with OUTHAUL_* environment overrides:
| Variable | Default | Purpose |
|---|---|---|
OUTHAUL_DATA_DIR | /var/lib/outhaul | SQLite DB and build work dirs |
OUTHAUL_LISTEN_ADDR | :8080 | Admin UI / API listen address |
OUTHAUL_DOCKER_HOST | (SDK env default) | Docker endpoint |
OUTHAUL_TRAEFIK_IMAGE | traefik:v3.3 | Managed Traefik image |
OUTHAUL_NETWORK | outhaul | Shared Docker network for app containers |
OUTHAUL_ACME_EMAIL | (empty) | Let's Encrypt email; set to enable HTTPS |
OUTHAUL_ACME_STAGING | false | Use the LE staging CA (testing) |
OUTHAUL_LOCAL_CA | false | serve HTTPS from the built-in local CA (LAN installs; excludes OUTHAUL_ACME_EMAIL) — see docs/LOCAL-CA.md |
OUTHAUL_HTTPS_PORT | 443 | Host port for HTTPS |
OUTHAUL_HEALTH_TIMEOUT | 60s | Deploy health-check deadline |
OUTHAUL_IMAGE_KEEP | 5 | Built images kept per app for rollback (0 keeps all) |
OUTHAUL_PRUNE_BUILD_CACHE | yes | Nightly prune of build cache unused for 7 days; it is daemon-wide, so set no on a host shared with other builds |
OUTHAUL_PUBLIC_URL | (empty) | Public base URL of the admin UI; enables GitHub App + webhook URLs |
OUTHAUL_SERVER_IP | (auto-detected) | Public IP embedded in generated sslip.io template domains |
Apps are expected to listen on $PORT (Outhaul sets it and points Traefik at it).
go test ./... # unit tests (fakes for Docker; no daemon needed)
go test -race ./... # with the race detector
The codebase is a single Go module. internal/core is pure domain logic (the
deployment state machine); Docker, the builder, and git cloning sit behind
interfaces with fakes, so the test suite never touches a real daemon.
Outhaul is free software licensed under the GNU Affero General Public License v3.0. You may use, modify, and self-host it freely; if you run a modified version as a network service, you must offer your users the corresponding source.
Copyright (C) 2026 the Outhaul authors.