MerlinCDN module
Uploads a certificate to MerlinCDN and binds it to one or more CDN distributions' canonical-names (CNAMEs). MerlinCDN certificates are immutable, so every renewal uploads a fresh certificate and re-binds.
Overview
- Transport: MerlinCDN REST API (
https://api.merlincdn.comby default; overridable per target). - Auth: a personal access token (Bearer) plus a merlin-organization-id and merlin-workspace-id header.
- Certificates are immutable: there is no update-in-place. Each deployment uploads a new certificate (a new numeric id) and re-points a CNAME at it.
- Rollback: supported — a rollback re-uploads a previous retained certificate version and re-binds the CNAME(s) to it (Rollback).
Prerequisites
- A MerlinCDN CDN distribution and a canonical-name (CNAME) under it, or a domain the module can create the CNAME for.
- A personal access token with certificate + canonical-name permissions, plus your organization and workspace ids.
Create the module credential
Settings → Distribution → Credentials → Add Credential → type MerlinCDN Token. Provide:
- Personal Access Token — the Bearer token.
- Organization ID — sent as
merlin-organization-id. - Workspace ID — sent as
merlin-workspace-id.
Stored as an envelope-encrypted JSON blob:
{
"personal_access_token": "<merlin PAT>",
"organization_id": "<merlin-organization-id>",
"workspace_id": "<merlin-workspace-id>"
}
Create a MerlinCDN target
Settings → Distribution → Targets → Add Target → type MerlinCDN:
- API Host (optional) — defaults to
https://api.merlincdn.com. - Bind to CNAME (default on) — off = upload-only mode (see below).
- Distribution bindings (optional) — one row per CDN distribution to bind. Both fields are live pickers: the Distribution is chosen by its display name and the Canonical Name (CNAME) from that distribution's live list, fetched from the MerlinCDN API with the selected credential (no hand-typed ids). Rows may be left empty — a certificate link can supply its own rows via Target Overrides. (Legacy rows saved with a
domaininstead of a CNAME id keep working; picking a CNAME replaces the domain.) - Delete old certificate (default on) — remove the previous certificate after the re-binds. Guarded: before deleting, the whole workspace is scanned — a previous certificate still bound to any other CNAME (e.g. a distribution you never added as a binding row) is kept with a warning; if the scan fails or the workspace exceeds 50 distributions, deletion is skipped (kept) with a warning.
Execution flow
- Authenticate with the token + organization/workspace headers.
- Upload the certificate (
POST /v1/cdn/certificates, multipartcert_file+key_file) → a new certificate id. If the upload fails nothing is bound. - Resolve each binding row's CNAME: explicit
canonical_name_id→ previous run → match/create bydomain. - Bind:
PATCH /v1/cdn/distributions/:id/canonical-names/:cnameIdwith the newcertificate_id, once per binding row. If every bind fails, the just-uploaded certificate is deleted (orphan cleanup); if only some fail, the successes stay bound and a re-run heals the rest. - If
delete_old_certis on and all binds succeeded, best-effort delete the previous certificate.
Multiple distributions & upload-only mode
A target carries a list of binding rows (bindings: distribution_id + canonical_name_id or domain per row). On every deploy the certificate is uploaded once, then each row's CNAME is re-pointed at it — so a single target pushes the same certificate to several CDN distributions. The rows are defaults and may be left empty: each certificate link can replace the whole list via Target Overrides (bindings), so one target serves many certificates, each with its own set of distributions. If some binds fail, the successful ones stay bound and re-running the distribution heals only the missing rows — the already-uploaded certificate is reused, never re-uploaded.
Set bind: false for upload-only mode: the certificate is uploaded and you manage the CNAME binding manually in the MerlinCDN panel. In this mode the binding fields are optional and delete_old_cert is ignored (the previous upload might be live in a manual binding). A rollback, like any deploy, only uploads — the binding stays operator-managed. Note: per-link Target Overrides can change the binding coordinates but cannot flip upload-only mode — bind is fixed at the target level.
Removing a binding row: the module never touches a CNAME it no longer manages. The removed row's CNAME keeps serving the previous certificate; the next run keeps that certificate (even with delete_old_cert on) and warns once — re-point or clean it up in the MerlinCDN panel, otherwise it serves an aging certificate until expiry.
Live-compare idempotency: every run compares each CNAME's live certificate_id on the CDN against the (re)used upload — already pointing at it ⇒ that binding is skipped (no PATCH); pointing elsewhere (a rollback, or a manual re-point in the panel) ⇒ it is re-bound, reusing the already-uploaded copy (no duplicate upload). Stored state is never trusted for the skip decision. The displaced certificate is never deleted (it was bound outside CertAutoPilot's deploy and may serve other CNAMEs).
Finding your organization & workspace ids: every API call carries them as the merlin-organization-id/merlin-workspace-id headers, but MerlinCDN's Postman docs inject them via a hidden pre-request script, so endpoint examples never show them. Copy the two header values from any panel request in browser DevTools → Network (app.merlincdn.com), or consult MerlinCDN's "API Usage" PDF / support.
Rollback
Supported via previous-version re-deploy: a rollback re-uploads a previous retained certificate version and re-binds every bound CNAME to it — the module's normal deploy path, fed older material. It no longer depends on delete_old_cert having been off: the previous version comes from CertAutoPilot's own artifact history, not from a certificate kept on the CDN. During a rollback run the delete_old_cert behavior is suppressed, so the newer certificate stays uploaded and rolling forward again is instant. Eligibility, the version picker, and auto-rollback: Rollback.
Because execute uploads before it binds, an upload failure never changes the live CDN state.
Troubleshooting
MERLINCDN_AUTH— the token, organization id, or workspace id was rejected.MERLINCDN_NOT_FOUND— the distribution or canonical-name id does not exist.MERLINCDN_UPLOAD/MERLINCDN_BIND— the certificate upload or the CNAME re-bind failed.MERLINCDN_QUOTA/MERLINCDN_CONNECT— rate limited or a connection failure (both retried).