Intended deployment architecture¶
The Content Cache charm manages an nginx instance that caches responses from a set of backends. It does not know how to discover backends, route by hostname or path, or terminate client-facing TLS on its own. It also does not cache personalized or session-dependent content — see Charm design and how it works under the hood for the static-only caching assumption. Those responsibilities are delegated to other charms over relations.
Content Cache cannot be deployed by itself.
It has no charm configuration options of its own — every behavior
is supplied entirely through the
cache-config relation. Deployed alone, with no related charm providing that relation, the
unit has no backends configured: nginx serves no cache locations, and the unit sits in
blocked status, waiting for a configuration-providing charm to be integrated.
This page describes the components required for a working deployment, the relations the charm supports, and how combining them enables or restricts specific use cases.
Required components¶
A working deployment needs at least two charms:
Content Cache — this charm. Runs nginx and performs the actual caching. Optionally terminates incoming TLS.
A backend-configuration charm, providing the
cache-configrelation. Exactly one of:Content Cache Backends Config — a subordinate charm deployed onto the Content Cache unit. It provides a direct, minimal way to describe a set of backends with no routing or ingress features.
Ingress configurator — a principal charm that translates its own configuration into the same
cache-configrelation data, but requires ahaproxy-routerelation before publishing a usable backend configuration. Paired with HAProxy, it adds an ingress layer in front of Content Cache: hostname/path routing, TLS termination, DDoS protection, and health-check-based retries.
Everything else described below is optional and layers additional capability onto this minimal pair.
High-level deployment diagram¶
The diagram below shows Content Cache with every relation it supports. Solid arrows are the
required, functional relation (cache-config); dashed arrows are optional or conditional
integrations.
flowchart TB
BackendConfig["Content Cache Backends Config<br/>or Ingress configurator"] -->|"cache-config<br/>(required)"| CC["Content Cache"]
CC -->|"proxied requests"| Backend["Backend / origin"]
CertProvider["Certificate provider<br/>(e.g. self-signed-certificates, lego)"] -.->|"certificates<br/>(optional: terminate TLS here)"| CC
CertProvider -.->|"receive-ca-cert<br/>(only for private/untrusted HTTPS backend CA)"| CC
COS["grafana-agent"] -.->|"cos-agent<br/>(optional: metrics & logs)"| CC
CC <-.->|"content-cache-peers<br/>(automatic, multi-unit port sync)"| CCPeer["Content Cache<br/>(other units)"]
Supported relations¶
Provides — relations where Content Cache offers data to a related charm:
Relation endpoint |
Interface |
Required? |
Purpose |
|---|---|---|---|
|
|
Yes |
Backend addresses and per-backend cache/health-check settings. Without this relation the charm has nothing to cache. |
|
|
Optional |
Ship metrics/logs to a Canonical Observability Stack (COS) via |
Requires — relations where Content Cache consumes data from a related charm:
Relation endpoint |
Interface |
Required? |
Purpose |
|---|---|---|---|
|
|
Optional |
Terminate TLS for incoming connections directly at Content Cache — standalone, or as a second TLS hop behind HAProxy (see use cases below). |
|
|
Conditionally |
Trust a CA so nginx can verify backend certificates. Required only if any configured backend uses an |
Peers — coordination between units of this application:
Relation endpoint |
Interface |
Required? |
Purpose |
|---|---|---|---|
|
|
Automatic |
Coordinates per-relation port allocation across all units of the same Content Cache application. Not user-configured. |
How relations combine to support use cases¶
Each scenario below builds on the previous one by adding one more relation.
Scenario 1: direct backend caching¶
flowchart LR
CCBC["Content Cache Backends Config<br/>(subordinate)"] -->|"cache-config"| CC["Content Cache"]
CC -->|"HTTP"| Backend["Backend / origin"]
The simplest supported deployment includes Content Cache Backends Config deployed as a subordinate
directly onto the Content Cache unit and describes one set of backends over plain HTTP. There
is no hostname/path-based routing and no TLS anywhere in the request path: each cache-config
relation is served on its own dedicated port (see Charm design and how it works under the hood), and callers
reach it directly at http://<content-cache-unit-ip>:<port>.
This pattern suits deployments with a small, fixed number of backend groups where clients (or
an existing load balancer) can address Content Cache units and ports directly, and where
routing decisions do not need to change dynamically. Content Cache’s own certificates
relation can also be used here, without HAProxy, if callers need to reach Content Cache over
HTTPS directly instead of plain HTTP — see How to connect to HTTPS backends.
Scenario 2: add an ingress with TLS termination at the front and a second TLS hop to Content Cache¶
flowchart LR
Client(["Client"]) -->|"🔒 HTTPS"| HAProxy["HAProxy"]
HAProxy -->|"🔒 HTTPS"| CC["Content Cache"]
CC -->|"HTTP"| Backend["Backend / origin"]
IC["Ingress configurator"] -.->|"cache-config"| CC
IC -.->|"haproxy-route"| HAProxy
Lego["certificate provider<br/>charm"] -.->|"certificates"| HAProxy
Lego -.->|"certificates"| CC
Lego -.->|"send-ca-cert"| HAProxy
Content Cache Backends Config is replaced with Ingress configurator, which pairs with HAProxy
over haproxy-route. A certificate provider such as lego integrates with HAProxy’s own
certificates relation, so the client speaks HTTPS to HAProxy. The certificate
provider charm also integrates directly with Content Cache’s own certificates relation,
so Content Cache presents its own TLS certificate and listens with ssl on its allocated
port; HAProxy then forwards over this second, independent HTTPS hop instead of plain HTTP,
encrypting traffic for its entire path rather than only from the client to HAProxy. For
HAProxy to trust this certificate, the same certificate provider also integrates with
HAProxy over the certificate_transfer interface (send-ca-cert → receive-ca-certs);
without it, HAProxy rejects Content Cache’s certificate and the hop fails. HAProxy adds
hostname/path-based routing, load balancing across Content Cache units, and DDoS
protection on top of what Scenario 1 provides. See How to connect to HTTPS backends and the
tutorial for a full walkthrough of this deployment.
Scenario 3: add HTTPS to the backend¶
flowchart LR
Client(["Client"]) -->|"🔒 HTTPS"| HAProxy["HAProxy"]
HAProxy -->|"🔒 HTTPS"| CC["Content Cache"]
CC -->|"🔒 HTTPS"| Backend["Backend / origin"]
IC["Ingress configurator"] -.->|"cache-config<br/>(backend-protocol=https)"| CC
IC -.->|"haproxy-route"| HAProxy
Lego["certificate provider<br/>charm"] -.->|"certificates"| HAProxy
Lego -.->|"certificates"| CC
Lego -.->|"send-ca-cert"| HAProxy
Lego -.->|"receive-ca-cert"| CC
%% Index 2: CC to Backend (Colors the line and the text)
linkStyle 2 stroke:#d946ef,stroke-width:3px,color:#d946ef
%% Index 3: IC to CC
linkStyle 3 stroke:#d946ef,stroke-width:3px,color:#d946ef
Building on Scenario 2 (including its HAProxy-to-Content-Cache TLS hop and the matching
send-ca-cert → receive-ca-certs trust relation), the backend is now addressed as an
https:// URL. Content Cache must trust the backend’s CA to verify its certificate, so the
certificate provider that issues the backend’s certificate also integrates over the
receive-ca-cert relation directly with Content Cache. This is a fourth, independent TLS
relation: the certificates issued for HAProxy’s client-facing listener and for Content Cache’s
own listener do not need to share a CA with the one used to protect the backend. For
simplicity, the diagram reuses the same certificate provider charm for all relations, but a
separate certificate provider instance for the backend’s CA works the same way. Without
receive-ca-cert, Content Cache still proxies to the backend, but nginx cannot verify its
certificate against a trusted CA, so upstream TLS verification fails and requests to that
backend return an error. See How to connect to HTTPS backends.
Observability¶
The cos-agent relation is additive to any of the scenarios above: integrating a
grafana-agent (or equivalent) principal charm collects nginx metrics and logs into COS
without changing how backends are configured. See How to enable COS.