AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
翻译准备中
尚未检测到更新。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.