# spindle-docker > **Early development / personal project** — This stack was built for personal use and tested on Ubuntu Linux. It has not been tested across a wide range of environments and may have rough edges or undocumented assumptions. Use it at your own risk. Docker Compose stack for self-hosting a [Tangled](https://tangled.org) spindle (CI runner) with [OpenBao](https://openbao.org) for secrets management. ``` . ├── docker-compose.yml ├── Dockerfile ├── init-openbao.sh # one-time vault bootstrap └── config/openbao/ ├── server/ │ └── server.hcl # OpenBao server config ├── proxy/ │ └── proxy.hcl # AppRole auto-auth proxy config └── spindle-policy.hcl # KV access policy for spindle ``` ## Prerequisites - Docker + Docker Compose - A domain or IP reachable by the Tangled network - Your ATProto DID ## Configuration Docker Compose loads `.env` automatically. Copy the sample and fill in the two required values: ```bash cp .env.sample .env ``` | Variable | Required | Default | Description | | -------------------------------------- | -------- | ------------------ | -------------------------------------------------- | | `SPINDLE_SERVER_HOSTNAME` | yes | — | Public hostname or IP (e.g. `spindle.example.com`) | | `SPINDLE_SERVER_OWNER` | yes | — | Your ATProto DID (e.g. `did:plc:xxxx`) | | `SPINDLE_PORT` | no | `6555` | Host port Spindle is exposed on | | `OPENBAO_PORT` | no | `8200` | Host port OpenBao is exposed on (local CLI access) | | `SPINDLE_SERVER_LISTEN_ADDR` | no | `0.0.0.0:6555` | Bind address inside the container | | `SPINDLE_SERVER_DB_PATH` | no | `/data/spindle.db` | SQLite database path inside the container | | `SPINDLE_PIPELINES_LOG_DIR` | no | `/var/log/spindle` | Pipeline log directory inside the container | | `SPINDLE_SERVER_SECRETS_OPENBAO_MOUNT` | no | `spindle` | KV v2 mount name | ## First-time setup **1. Configure environment** ```bash git clone https://tangled.org/danieldaum.net/spindle-docker cd spindle-docker cp .env.sample .env # Edit .env — set SPINDLE_SERVER_HOSTNAME and SPINDLE_SERVER_OWNER ``` **2. Start OpenBao** ```bash docker compose up -d openbao ``` Wait until you see the following line in the logs before continuing (`docker compose logs -f openbao`): ``` core: seal configuration missing, not initialized ``` **3. Initialize the vault** (once only) ```bash chmod +x init-openbao.sh ./init-openbao.sh ``` The script fixes permissions, initialises the vault, and configures AppRole automatically. It will print an **unseal key** and **root token** — save both somewhere safe, they are not stored anywhere and cannot be recovered. You will also be prompted to choose a Secret ID TTL (press enter for no expiry). **4. Start the full stack** Once the init script completes successfully: ```bash docker compose up -d ``` ## After a restart OpenBao seals itself on every restart. Run the unseal command once OpenBao is running (you can confirm it's ready when `docker compose logs openbao` shows `core: seal configuration missing, not initialized` or the container is healthy): ```bash docker compose exec openbao bao operator unseal ``` The proxy and Spindle will start automatically once OpenBao is unsealed and healthy. ## Verify ```bash curl http://localhost:6555/ # Spindle (should return the spindle welcome page) ``` ## Architecture ``` spindle (:6555) → openbao-proxy (:8201) → openbao (:8200) spindle → /var/run/docker.sock (pipeline containers run on the host daemon) ``` - **openbao** — secrets vault; sealed on every start - **openbao-proxy** — AppRole sidecar; auto-authenticates and exposes a token-authenticated proxy to spindle - **spindle** — the CI runner; starts only after the proxy is healthy ## Pinned versions All images and source are pinned to specific versions and verified by digest or commit SHA to prevent unexpected changes on rebuild. | Component | Version | Where | | ---------------- | ---------------------------- | -------------------- | | OpenBao | `2.5.2` | `docker-compose.yml` | | Go (builder) | `1.25.8-alpine3.23` | `Dockerfile` | | Alpine (runtime) | `3.23.3` | `Dockerfile` | | Spindle source | `v1.13.0-alpha` (`c3f60dc1`) | `Dockerfile` | To upgrade any component, update the tag/version and its corresponding `@sha256:...` digest (or commit SHA for Spindle). All versions are currently alpha — there are no stable Spindle releases yet. ## Notes - Port 8200 is exposed for local CLI access only (`127.0.0.1`). Remove that port mapping entirely if you don't need it. - TLS is disabled on both listeners. Put nginx or Caddy in front for production traffic. - Spindle mounts the Docker socket, so pipeline containers run on the **host** daemon. ## AppRole credential handling By default, the `openbao-approle` volume is mounted read-only (`:ro`) in the proxy container. This means the proxy can read the `role-id` and `secret-id` written by `init-openbao.sh` on every startup, but cannot delete them. The credentials persist on the volume indefinitely, so the proxy can re-authenticate automatically after any restart with no user intervention beyond unsealing OpenBao. The tradeoff: the `secret-id` is never rotated or deleted. For a self-hosted server where Docker volumes are only accessible to the server owner, this is a reasonable default. **If you want the secret-id deleted after first use** (higher security, more operational overhead): 1. In `docker-compose.yml`, remove `:ro` from the approle volume mount: ```yaml - openbao-approle:/openbao/approle ``` 2. After any restart or proxy container recreation, generate and write a new secret-id before starting the proxy: ```bash # Unseal first, then: SECRET_ID=$(docker compose exec -T openbao bao write \ -address=http://localhost:8200 -f -field=secret_id \ auth/approle/role/spindle/secret-id) docker run --rm \ -v "openbao-approle:/openbao/approle" \ --entrypoint="" \ alpine:3.23.3 \ sh -c "printf '%s' '$SECRET_ID' > /openbao/approle/secret-id \ && chown 100:1000 /openbao/approle/secret-id \ && chmod 640 /openbao/approle/secret-id" docker compose restart openbao-proxy ``` --- This repository is hosted on [Tangled](https://tangled.org/danieldaum.net/spindle-docker) and mirrored to [GitHub](https://github.com/daniel-daum/spindle-docker).