Skip to content

Deployment

Every service in the ecosystem uses one deployment pattern. This page describes it.

No host-specific detail here

Operational specifics — server addresses, usernames, secret configuration — are not part of this public site. They live in a closed runbook inside the relevant repository.

The general pattern

        Internet
   ┌─────────────────┐
   │   edge nginx    │  TLS termination · HSTS · rate limit
   │   (container)   │  each vhost → an internal service
   └────────┬────────┘
            │  shared Docker network
   ┌────────┴──────────────────────────────┐
   │                                       │
   ▼                ▼                      ▼
 app A            app B                 static site
 (Go + Next)     (Go + Next)            (nginx)
   │                │
   └────────┬───────┘
   PostgreSQL · Redis  (shared)

Core principles:

  1. One edge, many vhosts. A single nginx container on the outside owns ports 80/443. Each domain has its own server block and proxies to an internal service.
  2. Internal services are not exposed. Applications listen only on the shared Docker network, or bind to 127.0.0.1. They cannot be reached directly from outside.
  3. Shared infrastructure. One PostgreSQL and one Redis instance serve many applications; each application has its own database/schema.
  4. Docker Compose — each application has its own compose stack, attached to the shared network.

The edge nginx

The edge is responsible for:

Role Notes
TLS termination Let's Encrypt certificates, renewed automatically
HSTS max-age=63072000; includeSubDomains
Rate limiting By zone: auth (strict) · app (lenient) · api
Reverse proxy vhost → internal service
Rejecting unknown hosts Default server → drop the connection

Edge configuration is managed through git

The edge nginx conf.d directory is synced from a specific repository via git. Any manual edit on the host will be overwritten by git reset --hard at the next deploy.

Adding or changing a vhost is therefore done by committing to the repository and letting CI distribute it.

A second pattern — a service owning its own vhost

There is also a pattern where a vhost that concerns only one service lives in that service's repository rather than in a central file, and its deploy installs the config into the edge's conf.d as a separate file.

The advantage: a change is contained in one repository and does not wait on another team's deploy. The condition: the config must be fully self-contained — it may not depend on a rate-limit zone or default server defined in another file.

This site itself works that way — see This docs platform.

Certificates

  • Let's Encrypt, via certbot in webroot mode.
  • The ACME challenge is served over port 80 at /.well-known/acme-challenge/.
  • Renewal is automatic, weekly by cron; on success nginx is reloaded.

To add a new domain:

  1. Point DNS at the server.
  2. Add the domain to the ACME block on port 80.
  3. Obtain the certificate with certbot.
  4. Add the HTTPS vhost and point it at the certificate.
  5. Run nginx -t, then reload.

Static sites (documentation portals)

Documentation sites are pre-built static HTML. The simplest way to run them:

  • build site/ with MkDocs,
  • copy the result to the server,
  • have a small nginx:alpine container serve it,
  • have the edge nginx proxy to that container.

There is no application runtime, so resource use is low and there is little to break.

This site runs exactly like that — see This docs platform for the detail.

Deploying applications

Two patterns are in use for applications:

Pattern Notes
Build on the host CI syncs the repository to the host and runs docker compose build && up -d
Via a registry CI builds the image and pushes it to a registry; the host runs pull && up -d

The trend is towards fewer dependencies — there are cases that moved from an external registry to building on the host.

Partial deploys: CI determines which services to rebuild from the changed paths (paths-filter). A change in one application does not disturb the others.

Rollback

  • Static sites — unpack the archive of the previous build.
  • Applications — return to the previous image tag (it can be pinned with a <SVC>_IMAGE_TAG variable).

Health checks

Every service has a /health endpoint. Compose healthcheck definitions verify that infrastructure such as PostgreSQL and Redis is ready, and start the application only once they are.

Deployment checklist

  • [ ] DNS pointed correctly
  • [ ] Certificate issued and covered by automatic renewal
  • [ ] Edge vhost added and committed to the repository
  • [ ] nginx -t passes
  • [ ] Internal services not exposed externally
  • [ ] /health responding
  • [ ] Secrets via environment variables, absent from the repository
  • [ ] A clear way to roll back

For the automation detail, see CI/CD.