Getting started

# Overview

Deploy web apps in containers to any server running Docker, with zero downtime.

## What is dash?

dash deploys containerized web apps to your own servers — bare metal, cloud VMs, anything you can SSH into that runs Docker. It builds your image, pushes it to a registry, pulls it on your hosts, and switches traffic to the new containers with **zero downtime**, using [dash-proxy](https://github.com/zoolutions/dash-proxy) to swap requests between old and new versions seamlessly.

dash began as a fork of [basecamp/kamal](https://github.com/basecamp/kamal) and made a clean break in 2026. It ships the features upstream wouldn't merge — proxy load balancing, readiness gates, response caching, traffic shaping, SAN certificate batching, wildcard certificates — and moves at its own pace. Existing kamal deployments **upgrade in place**: the server's `.kamal/` directory renames itself to `.dash/` on the next deploy, `KAMAL_*` env vars are still set alongside their `DASH_*` twins, and a `.kamal/` directory in your repo is still read. In 4.0 the proxy container, network and volumes are renamed too — dash migrates each host itself, carrying the certificate store across, at the cost of a short outage per host while the container is replaced. See [From kamal](https://dash.zoolutions.llc/docs/from-kamal) for the migration story.

## How a deploy works

One command, five steps:

1. **Build** — `docker build` your app image (locally or on a remote builder), tagged with the current git SHA.
2. **Push** — push the image to your registry, then pull it on every server.
3. **Gate** — acquire the deploy lock and run readiness checks.
4. **Boot** — start new containers alongside the old ones, wait for the healthcheck to pass.
5. **Switch** — tell the proxy to route traffic to the new containers, then stop the old ones. Requests in flight finish on the old version; new requests hit the new one.

Everything is plain SSH + Docker — no control plane, no agents on your servers, no lock-in. The entire deployment is described in one file, `config/deploy.yml`.

```yaml
service: myapp
image: my-user/myapp

servers:
  - 192.168.0.1
  - 192.168.0.2

proxy:
  ssl: true
  host: app.example.com

registry:
  username: my-user
  password:
    - DASH_REGISTRY_PASSWORD
```

## Beyond kamal

The proxy features upstream does not have.

| Feature | What it gives you |
| --- | --- |
| [Load balancing](https://dash.zoolutions.llc/docs/load-balancing) | One host fronts the whole fleet — auto-activates for multi-host roles, shareable between apps. |
| [SAN batching & wildcards](https://dash.zoolutions.llc/docs/certificates) | Batch domains onto shared SAN certificates, issue wildcards via DNS-01, learn TLS hostnames from your app at runtime. |
| [Response caching](https://dash.zoolutions.llc/docs/caching) | An RFC 9111 shared cache in front of your app, administered with `dash proxy cache`. |
| [Traffic shaping](https://dash.zoolutions.llc/docs/traffic-shaping) | Rate limiting, IP allow/deny lists, per-path timeouts, read routing, session affinity, scale to zero. |
| Readiness gates | `dash doctor` diagnoses servers, registry, proxy, ports, DNS, and certificates before you deploy. |

The shared basics — services, roles, accessories, hooks — work the way upstream kamal's do, and the [Configuration](https://dash.zoolutions.llc/docs/configuration) pages here are generated from the gem itself, so they always match the installed version.

## Where next

- [Installation](https://dash.zoolutions.llc/docs/installation) — get the `dash` executable.
- [Quick start](https://dash.zoolutions.llc/docs/quick-start) — your first deploy in five minutes.
- [From kamal](https://dash.zoolutions.llc/docs/from-kamal) — switch an existing kamal app.
- [Commands](https://dash.zoolutions.llc/docs/commands) — the full CLI surface.