configuration for self hosting a spindle in docker
at main 166 lines 7.0 kB view raw view rendered
1# spindle-docker 2 3> **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. 4 5Docker Compose stack for self-hosting a [Tangled](https://tangled.org) spindle (CI runner) with [OpenBao](https://openbao.org) for secrets management. 6 7``` 8. 9├── docker-compose.yml 10├── Dockerfile 11├── init-openbao.sh # one-time vault bootstrap 12└── config/openbao/ 13 ├── server/ 14 │ └── server.hcl # OpenBao server config 15 ├── proxy/ 16 │ └── proxy.hcl # AppRole auto-auth proxy config 17 └── spindle-policy.hcl # KV access policy for spindle 18``` 19 20## Prerequisites 21 22- Docker + Docker Compose 23- A domain or IP reachable by the Tangled network 24- Your ATProto DID 25 26## Configuration 27 28Docker Compose loads `.env` automatically. Copy the sample and fill in the two required values: 29 30```bash 31cp .env.sample .env 32``` 33 34| Variable | Required | Default | Description | 35| -------------------------------------- | -------- | ------------------ | -------------------------------------------------- | 36| `SPINDLE_SERVER_HOSTNAME` | yes | — | Public hostname or IP (e.g. `spindle.example.com`) | 37| `SPINDLE_SERVER_OWNER` | yes | — | Your ATProto DID (e.g. `did:plc:xxxx`) | 38| `SPINDLE_PORT` | no | `6555` | Host port Spindle is exposed on | 39| `OPENBAO_PORT` | no | `8200` | Host port OpenBao is exposed on (local CLI access) | 40| `SPINDLE_SERVER_LISTEN_ADDR` | no | `0.0.0.0:6555` | Bind address inside the container | 41| `SPINDLE_SERVER_DB_PATH` | no | `/data/spindle.db` | SQLite database path inside the container | 42| `SPINDLE_PIPELINES_LOG_DIR` | no | `/var/log/spindle` | Pipeline log directory inside the container | 43| `SPINDLE_SERVER_SECRETS_OPENBAO_MOUNT` | no | `spindle` | KV v2 mount name | 44 45## First-time setup 46 47**1. Configure environment** 48 49```bash 50git clone https://tangled.org/danieldaum.net/spindle-docker 51cd spindle-docker 52cp .env.sample .env 53# Edit .env — set SPINDLE_SERVER_HOSTNAME and SPINDLE_SERVER_OWNER 54``` 55 56**2. Start OpenBao** 57 58```bash 59docker compose up -d openbao 60``` 61 62Wait until you see the following line in the logs before continuing (`docker compose logs -f openbao`): 63 64``` 65core: seal configuration missing, not initialized 66``` 67 68**3. Initialize the vault** (once only) 69 70```bash 71chmod +x init-openbao.sh 72./init-openbao.sh 73``` 74 75The 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). 76 77**4. Start the full stack** 78 79Once the init script completes successfully: 80 81```bash 82docker compose up -d 83``` 84 85## After a restart 86 87OpenBao 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): 88 89```bash 90docker compose exec openbao bao operator unseal <unseal_key> 91``` 92 93The proxy and Spindle will start automatically once OpenBao is unsealed and healthy. 94 95## Verify 96 97```bash 98curl http://localhost:6555/ # Spindle (should return the spindle welcome page) 99``` 100 101## Architecture 102 103``` 104spindle (:6555) → openbao-proxy (:8201) → openbao (:8200) 105spindle → /var/run/docker.sock (pipeline containers run on the host daemon) 106``` 107 108- **openbao** — secrets vault; sealed on every start 109- **openbao-proxy** — AppRole sidecar; auto-authenticates and exposes a token-authenticated proxy to spindle 110- **spindle** — the CI runner; starts only after the proxy is healthy 111 112## Pinned versions 113 114All images and source are pinned to specific versions and verified by digest or commit SHA to prevent unexpected changes on rebuild. 115 116| Component | Version | Where | 117| ---------------- | ---------------------------- | -------------------- | 118| OpenBao | `2.5.2` | `docker-compose.yml` | 119| Go (builder) | `1.25.8-alpine3.23` | `Dockerfile` | 120| Alpine (runtime) | `3.23.3` | `Dockerfile` | 121| Spindle source | `v1.13.0-alpha` (`c3f60dc1`) | `Dockerfile` | 122 123To 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. 124 125## Notes 126 127- Port 8200 is exposed for local CLI access only (`127.0.0.1`). Remove that port mapping entirely if you don't need it. 128- TLS is disabled on both listeners. Put nginx or Caddy in front for production traffic. 129- Spindle mounts the Docker socket, so pipeline containers run on the **host** daemon. 130 131## AppRole credential handling 132 133By 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. 134 135The 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. 136 137**If you want the secret-id deleted after first use** (higher security, more operational overhead): 138 1391. In `docker-compose.yml`, remove `:ro` from the approle volume mount: 140 141 ```yaml 142 - openbao-approle:/openbao/approle 143 ``` 144 1452. After any restart or proxy container recreation, generate and write a new secret-id before starting the proxy: 146 147 ```bash 148 # Unseal first, then: 149 SECRET_ID=$(docker compose exec -T openbao bao write \ 150 -address=http://localhost:8200 -f -field=secret_id \ 151 auth/approle/role/spindle/secret-id) 152 153 docker run --rm \ 154 -v "openbao-approle:/openbao/approle" \ 155 --entrypoint="" \ 156 alpine:3.23.3 \ 157 sh -c "printf '%s' '$SECRET_ID' > /openbao/approle/secret-id \ 158 && chown 100:1000 /openbao/approle/secret-id \ 159 && chmod 640 /openbao/approle/secret-id" 160 161 docker compose restart openbao-proxy 162 ``` 163 164--- 165 166This repository is hosted on [Tangled](https://tangled.org/danieldaum.net/spindle-docker) and mirrored to [GitHub](https://github.com/daniel-daum/spindle-docker).