Shipyard is a terminal UI for managing Docker containers across remote machines over SSH.
It is designed for a simple homelab workflow:
- see containers across multiple hosts in one view
- filter by host
- start, stop, and restart containers
- open live logs for any selected container
- deploy a local
docker-compose.ymland optional.envto one host
Shipyard uses your local ssh and scp commands instead of the Docker Remote API, so it follows the same SSH behavior that already works in your terminal.
Clone the repo, then install the binary:
./install.shThen launch it from anywhere:
shipyardIf you already have a config file in a custom location:
shipyard --config /path/to/shipyard.yaml- Aggregated container overview across all configured hosts
- Host filtering by configured hostname/name
- Running-only toggle or all-containers view
- Start, stop, and restart actions
- Live container logs with
Enter - Compose bundle deployment to a selected host
- First-run onboarding wizard when no config exists
- SnapVault-style text-first TUI with obvious selection markers
- Go 1.24+
sshandscpavailable on the local machine- SSH access to the remote hosts
- Docker installed on the remote hosts
docker composeavailable on the remote hosts
If no config exists, Shipyard starts in a setup wizard.
By default Shipyard uses:
./shipyard.yamlwhen a config exists in the current working directory~/.config/shipyard/shipyard.yamlotherwise
The wizard lets you:
- enter a host name
- enter an SSH address
- optionally enter a Tailscale address
- enter an SSH user
- set a deploy path such as
~/docker - test the SSH/Docker connection
- add multiple hosts
- save the config and continue into the main app
Run it with:
shipyardTo force Tailscale addresses on launch:
shipyard --tailscaleIf you prefer to prepare a config manually, you can still copy and edit the example:
cp shipyard.example.yaml shipyard.yaml
shipyard shipyard.yamlExample config: shipyard.example.yaml
Each host can optionally define:
tailscale_address: 100.x.y.zWhen present, Shipyard tries the normal address first and automatically falls back to tailscale_address if SSH/SCP cannot connect. Use --tailscale to force Tailscale routing from startup.
The default deploy base is:
~/docker
That is usually a better choice on Raspberry Pis than /opt/... because it is writable by the SSH user without extra setup.
Install the binary:
./install.shRun tests:
go test ./...Overview:
- shows containers across every configured host
- filters by host with
h/l - opens live logs with
Enter - starts, stops, and restarts the selected container
Deploy:
- selects a target host
- accepts a project name
- accepts a local absolute path to
docker-compose.yml - accepts an optional local absolute path to
.env - uploads files and runs
docker compose up -d
Logs:
- opens from the overview with
Enter - follows live container logs
- returns to the overview with
Escorb
Global:
1overview2deployqquitctrl+cquit
Overview:
j/kor↑/↓move selectionh/lfilter hostsatoggle running-only vs all containersEnteropen live logssstart selected containerxstop selected containerRrestart selected containery/nconfirm or cancel actions
Deploy:
tab/shift+tabor↑/↓move between fieldsh/lor←/→change the selected hostEnteradvance or activate the focused actionctrl+sorddeploy
Logs:
Escorbreturn to the overview
Setup:
tab/shift+tabor↑/↓move between fields and actionsEnteractivate the focused action
Shipyard uploads:
docker-compose.yml.envif provided
Then it runs:
docker compose --project-name <project-name> -f docker-compose.yml --env-file .env up -dIf no .env is provided, the --env-file flag is omitted.
By default projects are written to:
~/docker/<project-name>
- Host display order uses
hostname, thenname, thenaddress - Shipyard writes and reads
shipyard.yamldirectly - Connection testing in setup verifies SSH access and Docker visibility
- The current deploy flow updates files in place; it does not keep deploy history