diff --git a/src/content/blog/manifest.ts b/src/content/blog/manifest.ts index 655e05f..628096f 100644 --- a/src/content/blog/manifest.ts +++ b/src/content/blog/manifest.ts @@ -16,9 +16,9 @@ export interface PostMeta { export const POSTS: PostMeta[] = [ { slug: "why-shipdeck-replaced-docker", - title: "Why ShipDeck Replaced Docker at DashCaddy: One Command to Deploy", + title: "Why ShipDeck Replaced Docker: One Binary, No Disk Bloat", description: - "ShipDeck is the deployer DashCaddy uses for its own services — a single Go binary that replaces Docker, Docker Compose, and handwritten deploy scripts with one command. Here's what it does, why Docker wasn't the right fit for fleet deployments, and what makes ShipDeck better.", + "Docker was eating disk space for no reason, so we built ShipDeck — a single Go binary that replaces Docker, Docker Compose, and handwritten deploy scripts. No daemon, no layered filesystems, no bloat.", date: "2026-10-08", author: "DashCaddy Team", tags: ["shipdeck", "docker alternative", "deployment tool", "fleet deployer", "systemd deployment", "self-hosted deployment"], diff --git a/src/content/blog/why-shipdeck-replaced-docker.md b/src/content/blog/why-shipdeck-replaced-docker.md index 2497edf..48edeef 100644 --- a/src/content/blog/why-shipdeck-replaced-docker.md +++ b/src/content/blog/why-shipdeck-replaced-docker.md @@ -1,17 +1,17 @@ --- -title: "Why ShipDeck Replaced Docker at DashCaddy: One Command to Deploy" -description: "ShipDeck is the deployer DashCaddy uses for its own services — a single Go binary that replaces Docker, Docker Compose, and handwritten deploy scripts with one command. Here's what it does, why Docker wasn't the right fit for fleet deployments, and what makes ShipDeck better." +title: "Why ShipDeck Replaced Docker: One Binary, No Disk Bloat" +description: "Docker was eating disk space for no reason, so we built ShipDeck — a single Go binary that replaces Docker, Docker Compose, and handwritten deploy scripts with one command. No daemon, no layered filesystems, no bloat." date: "2026-10-08" author: "DashCaddy Team" tags: ["shipdeck", "docker alternative", "deployment tool", "fleet deployer", "systemd deployment", "self-hosted deployment"] readingMinutes: 7 --- -When you visit dashcaddy.net, or the DashCaddy dashboard hits its license server, or a monitoring check pings one of our backend services — most of those reached production through ShipDeck. It is the deployer DashCaddy uses for its own fleet services, and it replaced Docker for those deployments. +We created ShipDeck to replace Docker because Docker was eating disk space for no reason. -ShipDeck is a single Go binary. It takes a source repo and a TOML config file, then runs the full pipeline: build → package → SSH transfer → systemd install → Caddy reverse proxy → DNS record → live verification — one command, and the prior release is preserved so rollback is one command away after the first successful deploy. +Layered filesystems, image caches, build artifacts that never get cleaned up — a handful of Go services under Docker can quietly consume gigabytes. Multiply that across a fleet of hosts and the waste adds up fast. Our services compile to single binaries under 20 MB each. Wrapping them in a container that pulls a base image, layers a build, and leaves dangling images behind was burning storage we actually needed. -This is the story of why we built it and why Docker wasn't the right tool for that job. +ShipDeck is a single Go binary. It takes a source repo and a TOML config file, then runs the full pipeline: build → package → SSH transfer → systemd install → Caddy reverse proxy → DNS record → live verification — one command, and the prior release is preserved so rollback is one command away after the first successful deploy. No daemon. No registry. No layered filesystems. Just the binary you built, running under systemd. ## What DashCaddy needed from a deployer @@ -26,13 +26,15 @@ DashCaddy runs as a constellation of services across multiple hosts. There's the That is six steps that have to happen in order, and every step can fail. Before ShipDeck, each of these was a hand-run script or a Docker Compose file that covered maybe three of the six. -## Why Docker wasn't the right tool +## Why Docker was the wrong tool -Docker is excellent at running isolated processes. It solves the "works on my machine" problem by shipping the entire filesystem along with the process. For a homelab running third-party apps — Vaultwarden, Immich, a media server — that isolation is genuinely useful. Those apps ship as Docker images and updating them is a `docker pull` and a restart. +Docker is excellent at running isolated processes. It solves the "works on my machine" problem by shipping the entire filesystem along with the process. For a homelab running third-party apps — Vaultwarden, Immich, a media server — that isolation is genuinely useful. -But for your own software, which you already build from source, the container adds overhead that may not be worth the trade-off. +But for your own software, which you already build from source, the container is dead weight. Here's why we replaced it. -**The Docker daemon is another moving part.** Every Docker deploy requires the daemon to be running, healthy, and configured the same way on every host. It consumes memory even when idle. A systemd unit needs only systemd — which is already running on every Linux host and has been for a decade. +**Docker eats disk space.** A single Go service under Docker: base image (~100 MB), build layers (~200 MB), cached intermediates, dangling images from previous builds. The actual binary is 15 MB. Multiply across a half-dozen services on each host, and Docker was burning gigabytes storing nothing. ShipDeck lands the binary directly — one file, no layers, no cache to prune. + +**The Docker daemon is another moving part.** Every Docker deploy requires the daemon to be running, healthy, and configured the same way on every host. It consumes memory even when idle. A systemd unit needs only systemd — which is already running on every Linux host. **Docker images can be harder to debug.** When something goes wrong inside a container, the debugging path is `docker exec` into a filesystem that may not have the tools you need. A bare binary under systemd gives you `journalctl`, `coredumpctl`, and your own tooling — no extra layer. @@ -78,19 +80,7 @@ The `current` symlink is the atomic switch. If the new release fails verificatio ShipDeck is a CLI tool, not a daemon. You run `shipdeck deploy .` from a checkout; it does the work and exits. There is no persistent shipdeck process to monitor, upgrade, or debug. The journal is a flat JSONL file. The host registry is a TOML file at `/etc/shipdeck/hosts.toml`. -This means ShipDeck can be called from CI, from a Makefile, from a cron job, or from another CLI tool (DashCaddy's Deploys panel calls ShipDeck as its engine). Docker, by contrast, needs the daemon running on every host that builds or runs containers — and if the daemon is down, the pipeline is dead. - -## When to keep using Docker - -None of this means Docker is bad. Docker is the right choice for: - -- Running third-party apps that already ship as Docker images -- Services that need filesystem isolation from the host -- Development environments where containerization is part of the workflow - -DashCaddy itself still uses Docker for the apps it deploys — the 77 templates in the catalog all run as Docker containers. ShipDeck replaces Docker for *DashCaddy's own services*: the dashboard, the license server, the monitoring stack. Software we build from source and deploy to our own fleet. - -If you self-host apps with DashCaddy, Docker is still your runtime. If you want to deploy your own Go or Node services across your fleet the way DashCaddy does, ShipDeck is the tool. +This means ShipDeck can be called from CI, from a Makefile, from a cron job, or from another CLI tool. Docker, by contrast, needs the daemon running on every host that builds or runs containers — and if the daemon is down, the pipeline is dead. ## Getting started with ShipDeck