Skip to content

Install

Run on a Linux x86_64 or arm64 server:

Terminal window
curl -fsSL https://get.windlass.run | sudo sh

The installer:

  1. checks for Docker with the Compose v2 plugin, and if Docker is missing asks before installing it from get.docker.com (--yes agrees without asking, --no-docker refuses);
  2. installs Git with apt, dnf or apk if it is missing;
  3. installs Caddy from its official package repository unless --no-caddy is supplied or Caddy is already there. On a fresh Caddy install it replaces the packaged welcome Caddyfile with one that only enables the admin API on 127.0.0.1:2019, keeping the original as /etc/caddy/Caddyfile.default.bak. An existing Caddy is left as it is;
  4. creates the windlass system user and /var/lib/windlass/{projects,data,backups};
  5. installs windlass-docker-proxy.service on loopback port 2375 and does not grant the Windlass user direct socket or docker-group access;
  6. verifies the release checksum, installs the Windlass binary and hardened systemd unit, and starts both services.

The installer only prompts when it would install Docker. Under curl | sudo sh it asks on your terminal; with no terminal at all, it treats the answer as no. To answer yes up front when piping:

Terminal window
curl -fsSL https://get.windlass.run | sudo sh -s -- --yes

Supported flags are --yes, --version vX.Y.Z, --no-caddy, --no-docker, and --binary PATH (local/CI installation).

The install command pipes a script from the internet into sudo sh, so it is reasonable to want to look first. The script is short and plain:

Terminal window
curl -fsSL https://get.windlass.run -o install.sh
less install.sh
sudo sh install.sh

Every release is also signed with GitHub build provenance, which ties those exact bytes to the workflow and commit that produced them. This is worth more than the published checksums on their own: checksums.txt ships in the same release as the binaries, so anyone able to rewrite one could rewrite the other. The attestation is stored outside the release, where a release write cannot reach it.

To check a binary before installing it:

Terminal window
gh release download v0.37 --pattern 'windlass-linux-amd64'
gh attestation verify windlass-linux-amd64 --repo Sulaiman-Dauda/windlass

And the container image, addressed by digest rather than by a tag that can move:

Terminal window
gh attestation verify oci://ghcr.io/sulaiman-dauda/windlass:latest \
--repo Sulaiman-Dauda/windlass

Verification is silent on success and exits 0, so check the exit code rather than looking for output. A binary that has been altered by even one byte fails with a lookup error for its digest.

The default service listens on port 8080. Claim it using the one-time token printed in the journal:

Terminal window
sudo journalctl -u windlass | grep setup_token

Open http://<server-ip>:8080 temporarily, or use an SSH tunnel when port 8080 is firewalled. After signing in:

  1. create a DNS A/AAAA record such as windlass.example.com pointing at the server;
  2. open Settings, General, Panel domain;
  3. enter the hostname without https:// and save.

Windlass creates the targeted Caddy object windlass_panel_route and adds the hostname to Caddy certificate automation. It does not modify the Caddyfile or unrelated routes. Removing the setting deletes only that route. DNS must already resolve to the server and ports 80/443 must reach Caddy.

After the HTTPS hostname works, firewall public port 8080 or add a systemd override setting WINDLASS_ADDR=127.0.0.1:8080 and restart Windlass.

Variable Default Purpose
WINDLASS_ADDR :8080 Windlass HTTP listen address
WINDLASS_DATA /var/lib/windlass Platform database, keys, plugins, projects, and backups
WINDLASS_PROJECTS $WINDLASS_DATA/projects Authoritative Compose project directories
WINDLASS_LOG_LEVEL info debug, info, warn, or error
WINDLASS_CADDY_ADMIN http://127.0.0.1:2019 Caddy admin API used for targeted route updates
WINDLASS_PANEL_UPSTREAM 127.0.0.1:8080 Address Caddy dials for the Settings-managed panel route
WINDLASS_TRUSTED_PROXIES 127.0.0.0/8,::1/128 Proxy IPs/CIDRs allowed to supply client forwarding headers
WINDLASS_NO_SELF_UPDATE unset Disable binary self-update when set
WINDLASS_UPDATE_REPO Sulaiman-Dauda/windlass Repository checked for releases
WINDLASS_UPDATE_TOKEN unset GitHub token for a private update repository
DOCKER_HOST Docker client default Installer sets tcp://127.0.0.1:2375 in systemd

Configuration explains each one.

The Caddy admin API and Docker proxy must not be exposed publicly.

Terminal window
curl -fsSLO https://raw.githubusercontent.com/Sulaiman-Dauda/windlass/main/install/docker-compose.install.yaml
docker compose -f docker-compose.install.yaml up -d

This starts Windlass and the restricted socket proxy. Read the setup token from the container’s log:

Terminal window
docker compose -f docker-compose.install.yaml logs windlass | grep setup_token

It does not bundle Caddy. Application domains and the panel-domain setting remain unavailable until Windlass can reach an existing Caddy admin API. Set WINDLASS_CADDY_ADMIN and WINDLASS_PANEL_UPSTREAM to addresses valid from the Caddy/Windlass networks.

The example uses a named volume. Replace it with a host bind mount when SSH editing and filesystem-based recovery are required:

volumes:
- /var/lib/windlass:/var/lib/windlass

Container installs set WINDLASS_NO_SELF_UPDATE; update them by pulling a new image and recreating the Windlass container.

Terminal window
wget https://github.com/Sulaiman-Dauda/windlass/releases/latest/download/windlass-linux-amd64
chmod +x windlass-linux-amd64
sudo WINDLASS_DATA=/var/lib/windlass ./windlass-linux-amd64

A bare process needs equivalent filesystem permissions, Docker connectivity, Caddy access, and process supervision. The systemd installer is safer for normal servers.

The Update now button under Settings, System is offered only on Linux binary installations with self-update enabled. It downloads the release binary and checksums.txt, refuses the update unless the SHA-256 matches, and restarts. The hardened systemd unit cannot write /usr/local/bin, so the panel stages the binary in its data directory and the unit’s start hook swaps it in, keeping the old one as /usr/local/bin/windlass.previous. Application containers are not restarted.

Manual rollback:

Terminal window
sudo mv /usr/local/bin/windlass.previous /usr/local/bin/windlass
sudo systemctl restart windlass
Terminal window
sudo systemctl disable --now windlass
sudo systemctl disable --now windlass-docker-proxy
sudo rm /usr/local/bin/windlass

Do not delete /var/lib/windlass/projects if you want to retain stack files. Existing Docker containers keep running. Caddy’s in-memory routes continue until Caddy reloads/restarts; make any route needed permanently without Windlass part of the administrator-owned Caddyfile.

See Life without the panel.