# Install Shipyard on a Raspberry Pi

Shipyard is one static binary and one SQLite file. A Raspberry Pi 4 or 5 runs it comfortably.

## What you need

- Raspberry Pi 4 or 5 with 64-bit Raspberry Pi OS or Ubuntu Server
- An SSD or a good SD card — the queue and logs are written to local disk
- SSH access with `sudo`

## 1. Install the binary

Take `shipyard-linux-arm64` from [Downloads](/downloads/) (`shipyard-linux-armv7` on a 32-bit system) and copy it to the Pi:

```sh
scp shipyard-linux-arm64 pi@raspberrypi.local:/tmp/shipyard
ssh pi@raspberrypi.local
sudo install -m 0755 /tmp/shipyard /usr/local/bin/shipyard
```

## 2. Create the service user

```sh
sudo useradd --system --home-dir /var/lib/shipyard --shell /usr/sbin/nologin shipyard
sudo install -d -m 0750 -o root -g shipyard /etc/shipyard
```

## 3. Write the configuration

`/etc/shipyard/.shipyard.yaml`:

```yaml
listen: 127.0.0.1:8080
projects:
  - id: my-app
    repository: you/my-app
    enabled: true
    issue_label: agent-ready
    release_interval: 48h
    content:
      interval: 168h
      publish_mode: pull_request
storage:
  path: /var/lib/shipyard/shipyard.db
logging:
  directory: /var/lib/shipyard/logs
runtime:
  embedded_worker: true
  api_token_env: SHIPYARD_API_TOKEN
observability:
  metrics: true
pipelines:
  project-check:
    steps:
      - id: config
        type: diagnostic
        timeout: 30s
```

## 4. Set the API token

```sh
echo "SHIPYARD_API_TOKEN=$(openssl rand -hex 32)" | sudo tee /etc/shipyard/shipyard.env > /dev/null
sudo chown root:shipyard /etc/shipyard/shipyard.env /etc/shipyard/.shipyard.yaml
sudo chmod 0640 /etc/shipyard/shipyard.env /etc/shipyard/.shipyard.yaml
```

## 5. Add the systemd unit

`/etc/systemd/system/shipyard.service`:

```ini
[Unit]
Description=Shipyard portfolio control plane
Wants=network-online.target
After=network-online.target

[Service]
Type=simple
User=shipyard
Group=shipyard
WorkingDirectory=/var/lib/shipyard
StateDirectory=shipyard
StateDirectoryMode=0750
EnvironmentFile=-/etc/shipyard/shipyard.env
ExecStart=/usr/local/bin/shipyard -config /etc/shipyard/.shipyard.yaml
Restart=on-failure
RestartSec=5s
TimeoutStopSec=15s
SyslogIdentifier=shipyard
UMask=0077
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6

[Install]
WantedBy=multi-user.target
```

```sh
sudo systemctl daemon-reload
sudo systemctl enable --now shipyard
```

## 6. Check it

```sh
curl http://127.0.0.1:8080/healthz
curl http://127.0.0.1:8080/readyz
journalctl -u shipyard -f
```

## 7. Run the first pipeline

```sh
TOKEN=$(sudo sed -n 's/^SHIPYARD_API_TOKEN=//p' /etc/shipyard/shipyard.env)
curl -X POST http://127.0.0.1:8080/api/v1/runs \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: pi-first-check-001' \
  -d '{"project_id":"my-app","kind":"pipeline","task":"project-check","input":""}'
```

The run goes through the queue, the embedded worker picks it up and the result is in the run list:

```sh
curl -H "Authorization: Bearer $TOKEN" "http://127.0.0.1:8080/api/v1/runs?limit=1"
```

## Open the UI from your laptop

Shipyard listens on loopback. Tunnel it:

```sh
ssh -L 8080:127.0.0.1:8080 pi@raspberrypi.local
```

Then open `http://127.0.0.1:8080` and paste the token.

## Keep the SD card alive

Cap the journal in `/etc/systemd/journald.conf`:

```ini
SystemMaxUse=200M
```

Shipyard's own log and run retention is set under `retention:` in the [configuration](/configuration/).
