Simple remote server management system, based on bash scripts, and works with anything.
  • C++ 96.8%
  • Shell 2.9%
  • CMake 0.1%
Find a file
j 71638c9863
All checks were successful
Build-Test-Publish / build (linux/amd64) (push) Successful in 1m0s
Build-Test-Publish / build (linux/arm64) (push) Successful in 2m13s
check-updates: cap parallel installs at 4 to stay under MaxStartups
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>
2026-06-02 23:39:22 +12:00
.example dropshell release 2025.0524.1149 2025-05-24 11:49:24 +12:00
.gitea/workflows Update CI registry from gitea.jde.nz to forge.jde.nz 2026-04-05 14:59:32 +12:00
.vscode Major refactor 2025-12-30 08:43:06 +13:00
source check-updates: cap parallel installs at 4 to stay under MaxStartups 2026-06-02 23:39:22 +12:00
.gitignore fast check for autocomplete! 2025-09-30 13:56:47 +13:00
.gitmodules tidying 2025-04-22 20:35:09 +12:00
build.sh Fix binary extraction to use docker cp instead of volume mount for docker-in-docker compatibility 2026-04-27 18:45:26 +12:00
CLAUDE.md docs: Update 3 files 2025-09-02 12:15:48 +12:00
Dockerfile.dropshell-build Touch agent and prebuild files to force autogen regeneration in Docker build 2026-05-01 22:28:22 +12:00
dropshell-install.sh Update dropshell-install.sh 2025-09-04 09:00:08 +12:00
dropshell-server-autosetup.sh Add NTP client setup to server autosetup script 2026-05-09 22:33:51 +12:00
LICENSE . 2025-04-21 09:58:37 +12:00
publish.sh Strip and UPX-pack release binary; keep unstripped copy for symbols 2026-05-08 17:19:54 +12:00
README.md refactor(cli): switch to SERVICE SERVER argument order and split serve 2026-05-14 12:07:31 +12:00
TEMPLATES.md refactor(cli): switch to SERVICE SERVER argument order and split serve 2026-05-14 12:07:31 +12:00
test.sh test: Add 1 and update 2 files 2025-08-17 21:12:13 +12:00

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:

  1. apt install curl wget jq
  2. curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh && rm get-docker.sh
  3. useradd -m dropshell && usermod -aG docker dropshell && chsh -s /bin/bash dropshell
  4. Put appropriate ssh keys in /home/dropshell/.ssh/authorized_keys
  5. 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:

  1. dropshell create-server SERVERNAME
  2. dropshell edit-server SERVERNAME
  3. dropshell install-server SERVERNAME

Install Services

Create and install a service

  1. ds list-templates -- see what templates are available to install.
  2. ds create-service SERVICENAME TEMPLATE SERVERNAME
  3. ds edit SERVICENAME SERVERNAME
  4. Edit other config files if needed.
  5. ds install SERVICENAME SERVERNAME
  6. ds 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:

  1. wm-registry first (VPN registry must be up before gateways can re-register)
  2. wm-gateway(s) next (VPN gateways may provide SSH connectivity to other servers)
  3. 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

  1. Deploy ds-backup-server on 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
    
  2. 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
  3. Schedule automated daily backups per server:

    ds schedule-backup myserver 02:00
    

    This 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=N via nvidia-smi)
  • GPU_MODEL=amd → first AMD/Radeon card (--device /dev/dri/renderDN --group-add GID via lspci)
  • GPU_MODEL=intel → first Intel GPU (same as AMD)
  • GPU_MODEL=4080 → specific model substring, tried against nvidia-smi then lspci

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.