rolling_deployment

package module
v0.9.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 25, 2026 License: MIT Imports: 13 Imported by: 0

README

caddy-rolling-deployment

A Caddy implementation of a rolling deployment strategy for Docker containers: a webhook updates a named container to a new image, one host at a time, preserving the container's existing configuration.

  • Module ID: http.handlers.rolling_deployment
  • Caddyfile directive: rolling_deployment
  • Go module: github.com/multionlabs/caddy-rolling-deployment
  • Caddy docs: http.handlers.rolling_deployment

What it does

When a deploy webhook is received, the plugin:

  1. Finds configured Docker hosts where a container with the given name is already running.
  2. On each of those hosts (in order), pulls the new image, snapshots the running container's spec, renames the old container aside, starts a replacement with the same name/spec and the new image, then removes the backup on success.
  3. Stops on the first host failure (fail-fast). Hosts already updated stay on the new image (HTTP 207); a failed host is rolled back when possible.

Preserved from the previous container (when present): command/entrypoint, env, labels, mounts (bind + named volumes), published ports, networks, restart policy, and related create options.

Install

xcaddy build \
  --with github.com/multionlabs/caddy-rolling-deployment

Pin a version:

xcaddy build \
  --with github.com/multionlabs/caddy-rolling-deployment@v0.9.0

Local checkout:

xcaddy build \
  --with github.com/multionlabs/caddy-rolling-deployment=.

Sample Caddyfile

deploy.example.com {
	rolling_deployment {
		secret {$ROLLING_DEPLOY_SECRET}
		docker_hosts unix:///var/run/docker.sock
		# docker_hosts tcp://host-a:2375 tcp://host-b:2375
	}
}
Option Required Description
secret yes Shared secret; must match the webhook path segment.
docker_hosts no One or more Docker Engine API endpoints. Defaults to unix:///var/run/docker.sock.

Directive order is registered in code (before respond).

Webhook

POST|GET /webhooks/rolling-deployment/{secret}/{service_container}/{service_image}
  • service_container — exact Docker container name (must already be running on at least one configured host).
  • service_image — image reference; may contain / (e.g. ghcr.io/org/app:1.2.3).

Example (typically called by a CI/CD job after publishing an image):

curl -si \
  "https://deploy.example.com/webhooks/rolling-deployment/${ROLLING_DEPLOY_SECRET}/api/ghcr.io/acme/api:1.2.3"
Response

JSON body (success / partial):

{
  "partial": false,
  "hosts": [
    { "host_index": 0, "ok": true }
  ]
}

Raw Docker host URLs, daemon error text, and the request's container/image strings are not echoed in the response (details stay in Caddy logs).

Status Meaning
200 All selected hosts updated.
207 Partial: at least one host updated, then the roll stopped.
400 Bad path/image, or container not running on any configured host.
401 Secret mismatch.
409 A deploy for the same container name is already in progress.
422 Image/spec incompatible; previous container restored when possible.
502 Docker/infrastructure failure.

Behavior notes

  • Selection: only hosts where the named container is currently running are updated; others are skipped.
  • Concurrency: overlapping deploys of the same container name return 409; different names can run in parallel.
  • Rollback: on a failed recreate/start, the plugin attempts to restore the previous container (backup name pattern: {name}_rollback_YYYYMMDDHHMMSS).
  • Secrets: treat the webhook secret like a deploy credential; prefer HTTPS in production.

Development

go test ./...
golangci-lint run ./...

Integration scenarios (requires Docker + a test Caddy from tests/with-caddy/run.sh):

./tests/with-caddy/scenario-basic.sh
./tests/with-caddy/scenario-spec-preservation.sh
./tests/with-caddy/scenario-rollback.sh
./tests/with-caddy/scenario-concurrency.sh

Release

git tag v0.9.0
git push origin v0.9.0

License

MIT — see LICENSE.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Middleware

type Middleware struct {
	// Secret is the shared webhook credential. It must match the `{secret}`
	// path segment on every deploy request. Required.
	Secret string `json:"secret,omitempty"`

	// DockerHosts is the list of Docker Engine API endpoints to consider
	// (for example `unix:///var/run/docker.sock` or `tcp://host:2375`).
	// Only hosts where the named container is already running are updated.
	// Defaults to the local Docker socket when omitted.
	DockerHosts []string `json:"docker_hosts,omitempty"`
	// contains filtered or unexported fields
}

Middleware is an HTTP handler that implements a rolling deployment strategy for Docker containers. It exposes a deploy webhook and leaves all other requests to the next handler in the chain.

### Caddyfile example

Enable the webhook on `deploy.example.com` (HTTPS via Caddy's automatic certificates). Caddy must be able to reach each Docker Engine API endpoint listed in `docker_hosts`:

```

deploy.example.com {
    rolling_deployment {
        secret {$ROLLING_DEPLOY_SECRET}
        docker_hosts unix:///var/run/docker.sock tcp://docker-b.internal:2375
    }
}

```

### Who triggers a deploy, and how

Typically a CI/CD job (GitHub Actions, GitLab CI, etc.) or an operator calls the webhook after publishing a new image. Any HTTP method is accepted; `GET` or `POST` are common:

```

curl -si \
  "https://deploy.example.com/webhooks/rolling-deployment/${ROLLING_DEPLOY_SECRET}/api/ghcr.io/acme/api:1.2.3"

```

Path shape:

`/webhooks/rolling-deployment/{secret}/{container}/{image}`

  • `{secret}` must match the configured `secret` (constant-time compared).
  • `{container}` is the exact Docker **container name** that must already be running (e.g. `api`).
  • `{image}` is the new image reference and may contain `/` (e.g. `ghcr.io/acme/api:1.2.3`).

### Exact effect of that request

For the example above, assuming a container named `api` is running on both configured hosts and currently uses `ghcr.io/acme/api:1.2.2`:

  1. Hosts **without** a running `api` container are skipped.
  2. On each selected host, in order: pull `ghcr.io/acme/api:1.2.3`, snapshot the running `api` container's create config, rename it aside as `api_rollback_YYYYMMDDHHMMSS`, create and start a new `api` container with the **same** env/labels/mounts/ports/networks/restart policy but the new image, then remove the backup on success.
  3. Fail-fast: if a later host fails, earlier hosts stay on `1.2.3` (HTTP `207`), and the failed host is rolled back when possible.
  4. A second overlapping deploy for the same container name returns HTTP `409`.

Successful responses are JSON listing per-host outcomes by `host_index` (index into `docker_hosts`). Docker host URLs and daemon error text are not returned in the body; see Caddy logs for details.

func (Middleware) CaddyModule

func (Middleware) CaddyModule() caddy.ModuleInfo

CaddyModule returns the Caddy module information.

func (*Middleware) Cleanup

func (m *Middleware) Cleanup() error

Cleanup implements caddy.CleanerUpper, closing all Docker clients.

func (*Middleware) Provision

func (m *Middleware) Provision(ctx caddy.Context) error

Provision implements caddy.Provisioner.

func (Middleware) ServeHTTP

func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error

ServeHTTP implements caddyhttp.MiddlewareHandler.

Only /webhooks/rolling-deployment/{secret}/{service_container}/{service_image} is handled here (terminal). All other requests are passed to the next handler.

func (*Middleware) UnmarshalCaddyfile

func (m *Middleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

UnmarshalCaddyfile implements caddyfile.Unmarshaler.

func (*Middleware) Validate

func (m *Middleware) Validate() error

Validate implements caddy.Validator.

Directories

Path Synopsis
Package deploy performs rolling container deployments across one or more Docker hosts, using docker.Client instances for the underlying operations.
Package deploy performs rolling container deployments across one or more Docker hosts, using docker.Client instances for the underlying operations.
Package docker provides Docker Engine operations used by rolling deployments.
Package docker provides Docker Engine operations used by rolling deployments.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL