- C++ 96.8%
- Shell 2.9%
- CMake 0.1%
Previously the thread pool was sized to the number of items, so updating 30 services spun up 30 concurrent SSH-bound installs against the relevant servers. OpenSSH's default MaxStartups (10:30:60) starts dropping connections at the 10th concurrent client, surfacing as intermittent "ssh_exchange_identification: Connection closed" failures partway through a bulk update. Now capped at min(items, 4) for the agent-check, agent-update, unique- service-install, and group-install phases. Override via env when the forge is tuned higher: DROPSHELL_MAX_PARALLEL=8 ds check-updates. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| .example | ||
| .gitea/workflows | ||
| .vscode | ||
| source | ||
| .gitignore | ||
| .gitmodules | ||
| build.sh | ||
| CLAUDE.md | ||
| Dockerfile.dropshell-build | ||
| dropshell-install.sh | ||
| dropshell-server-autosetup.sh | ||
| LICENSE | ||
| publish.sh | ||
| README.md | ||
| TEMPLATES.md | ||
| test.sh | ||
Dropshell
A system management tool for server operations, written in C++.
Installation
curl -fsSL https://getbin.xyz/dropshell-install | bash
This installs as dropshell for the local user, with a symbolic link ds. You'll need to run:
~/.local/bin/dropshell edit
~/.local/bin/dropshell install
source ~/.bashrc
to configure dropshell and install the local components.
Remote Server Setup
Initial setup
Auto setup script which creates a dropshell user, and includes installing docker if not already present. As root:
curl -fsSL https://getbin.xyz/dropshell-server-autosetup | bash
Manual steps:
apt install curl wget jqcurl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh && rm get-docker.shuseradd -m dropshell && usermod -aG docker dropshell && chsh -s /bin/bash dropshell- Put appropriate ssh keys in
/home/dropshell/.ssh/authorized_keys - Test ssh'ing into the server.
Configure and Use Remote Server
Add to local dropshell configuration, and install remote agent
Back on the dropshell host:
dropshell create-server SERVERNAMEdropshell edit-server SERVERNAMEdropshell install-server SERVERNAME
Install Services
Create and install a service
ds list-templates-- see what templates are available to install.ds create-service SERVICENAME TEMPLATE SERVERNAMEds edit SERVICENAME SERVERNAME- Edit other config files if needed.
ds install SERVICENAME SERVERNAMEds list
The service should now be seen to be running.
Checking for Updates
ds check-updates # or: ds outdated
This pulls all template sources (in parallel), checks every server's agent and service hashes against the local templates, and presents a summary of what's outdated.
You can then choose to update agents only, or agents and services. Updates run in a specific order to maintain infrastructure availability:
- wm-registry first (VPN registry must be up before gateways can re-register)
- wm-gateway(s) next (VPN gateways may provide SSH connectivity to other servers)
- Everything else in parallel
Template source repos are pulled once per session and cached, so parallel service installs never trigger redundant git pulls.
Backups
Dropshell uses restic for encrypted, deduplicated backups stored on a central backup server.
Setup
-
Deploy
ds-backup-serveron any server in your tailnet:ds create-service ds-backup-server ds-backup-server myhost ds edit ds-backup-server myhost # set BACKUP_PASSWORD ds install ds-backup-server myhost -
Configure
~/.dropshell/dropshell.json:{ "backup_server_url": "rest:http://<tailscale-ip>:8000", "backup_password": "<master-password>" }backup_server_url: restic REST server URL (rest:prefix is the restic backend scheme)backup_password: master password — dropshell derives unique per-server passwords via HMAC-SHA256
-
Schedule automated daily backups per server:
ds schedule-backup myserver 02:00This installs a crontab entry on the remote server. Re-run with a different time to update it. Remove with
ds unschedule-backup myserver.
Commands
ds backupdata SERVICE SERVER # Manual backup to restic server
ds backupdata all SERVER # Backup all services on a server
ds restoredata SERVICE SERVER latest # Restore from latest snapshot
ds restoredata SERVICE SERVER <snapshot> # Restore from specific snapshot ID
ds schedule-backup SERVER [HH:MM] # Schedule nightly backups (default 02:00)
ds unschedule-backup SERVER # Remove scheduled backups
Each server/service combination gets its own restic repository (deduplication is within a single repo only, not across services or servers). The backup server web dashboard (port 8088) shows all repositories, snapshot counts, and disk usage. Backup data is encrypted at rest; the REST server runs without auth and relies on Tailscale for network isolation.
Template authors: implement backup.sh and restore.sh in your template to make a service backupable. See TEMPLATES.md for the contract.
Command Reference
General
| Command | Description |
|---|---|
ds help [COMMAND] |
Show help, or detailed help for a specific command |
ds version |
Show dropshell version |
ds edit |
Edit dropshell global configuration |
ds edit override |
Edit local overrides |
ds list [SERVICE] [SERVER] |
List servers, services, and their status |
ds search STRING |
Search for services matching a pattern across all servers |
Server Management
| Command | Description |
|---|---|
ds create-server [SERVER] |
Create a new server entry |
ds edit-server SERVER |
Edit server configuration |
ds install-server SERVER|all |
Install/update the remote agent on a server (or all servers + host) |
ds enable SERVER |
Re-enable a previously disabled server |
ds disable SERVER |
Disable a server while keeping all data intact |
ds ssh SERVER |
SSH into a server |
ds hostinfo SERVER |
Display hardware and system info for a server |
ds health SERVER |
Check health of all services on a server |
Service Lifecycle
Service-related commands always take SERVICE first, then SERVER. This lets
you tab-complete the service name first and discover which server it lives on
without remembering. Server-only operations remain <command> SERVER.
| Command | Description |
|---|---|
ds create-service SERVICE TEMPLATE SERVER |
Create a new service from a template |
ds edit SERVICE SERVER |
Edit service configuration |
ds install SERVICE|all SERVER |
Install or update a service |
ds uninstall SERVICE|all SERVER |
Uninstall a service (keeps config and data) |
ds destroy SERVICE|all SERVER |
Destroy a service, erasing everything local and remote |
ds phoenix SERVICE SERVER |
Destroy and recreate a service, preserving configuration |
Service Operations
| Command | Description |
|---|---|
ds start SERVICE|all SERVER |
Start a service |
ds stop SERVICE|all SERVER |
Stop a service |
ds log SERVICE SERVER |
View logs for a service |
ds ssh SERVICE SERVER |
SSH into a service's docker container |
ds run SERVICE SERVER SCRIPT [ARGS...] |
Run a template script for a service |
ds reload-config SERVICE SERVER |
Sync config and hot-reload on the remote server |
ds validate-config SERVICE SERVER |
Sync config and validate it on the remote server |
ds migrate SERVICE SOURCE_SERVER DEST_SERVER |
Migrate a service between servers |
Templates
| Command | Description |
|---|---|
ds list-templates |
List all available templates and their sources |
ds add-template [PATH] |
Register a directory as a template source |
ds create-template TEMPLATE |
Create a new template |
ds validate-template TEMPLATE |
Validate a template's structure and syntax |
ds pull |
Pull/clone all git-backed template sources |
Groups
| Command | Description |
|---|---|
ds create-group GROUP |
Create a new server group for replicated services |
Backups
| Command | Description |
|---|---|
ds backupdata SERVICE|all SERVER |
Backup service data to restic server |
ds restoredata SERVICE SERVER SNAPSHOT|latest |
Restore from a backup snapshot |
ds list-backups TEMPLATE |
List backup snapshots for a template across all servers |
ds schedule-backup SERVER [HH:MM] |
Schedule daily backups (default 02:00) |
ds unschedule-backup SERVER |
Remove scheduled backups |
Updates
| Command | Description |
|---|---|
ds check-updates |
Check for outdated agents and services, offer to update |
Remote Scripts
| Command | Description |
|---|---|
ds rootscript SERVER|all SCRIPT [ARGS...] |
Run a script on a remote server as root (falls back to dropshell user) |
Built-in scripts: safe-update (safely update system packages), sysinfo (gather system info).
Utilities
| Command | Description |
|---|---|
ds hash [FILE|DIRECTORY] |
Hash a file or directory |
ds info-json |
Output JSON with hardware info for all servers |
GPU Support
Templates can select a GPU by model name rather than device index. The _gpu_docker_flags helper is available in all template scripts via common.sh and resolves the correct docker flags at deploy time:
GPU_MODEL=nvidia→ first NVIDIA card (--gpus device=Nvianvidia-smi)GPU_MODEL=amd→ first AMD/Radeon card (--device /dev/dri/renderDN --group-add GIDvialspci)GPU_MODEL=intel→ first Intel GPU (same as AMD)GPU_MODEL=4080→ specific model substring, tried againstnvidia-smithenlspci
Usage in a template's start.sh:
GPU_FLAGS=$(_gpu_docker_flags "$GPU_MODEL")
docker run ${GPU_FLAGS} ...
Fail-fast check in install-pre.sh (runs while the old service is still live):
_gpu_docker_flags "$GPU_MODEL" > /dev/null
For docker-compose templates, resolve to an index first:
export GPU_DEVICE_INDEX=$( ... ) # resolved from GPU_MODEL
docker compose up -d
# compose YAML: device_ids: ['${GPU_DEVICE_INDEX}']
Tailscale Per-Service Isolation
When multiple services run on the same host, each can get its own Tailscale node identity and ACL tags — without LXC or VMs — using a lightweight sidecar pattern built into common.sh.
Set in service.env (all blank = disabled, no overhead):
TAILSCALE_AUTH_KEY= # pre-authorized key from Tailscale admin console
TAILSCALE_HOSTNAME= # tailnet hostname (defaults to CONTAINER_NAME)
TAILSCALE_TAGS=tag:my-svc # must match the key's allowed tags
TAILSCALE_USERSPACE=false # true if /dev/net/tun unavailable
In start.sh (docker run templates):
docker run $(_ts_network_args) ...
In start.sh + compose YAML (docker-compose templates):
export TS_NETWORK_MODE=$(_ts_compose_network_mode)
docker compose up -d
network_mode: ${TS_NETWORK_MODE:-host}
In uninstall.sh:
_ts_remove_sidecar
When disabled, _ts_network_args returns --network host (or a custom fallback) with no sidecar started. When enabled, a tailscale/tailscale:stable container named ${CONTAINER_NAME}_ts is started automatically, the service container joins its network namespace, and it appears as a separate node on the tailnet with its own IP and ACL tags — independent of the host's Tailscale node.