Repository navigation
cmd ssl
Manage SSL certificates for ɳSelf services and custom domains.
nself ssl <subcommand> [flags]
nself ssl manages the SSL certificates used by nginx to serve HTTPS traffic for all ɳSelf services. Certificates are generated automatically during nself build, but you can use this command to check their status or force regeneration without a full rebuild.
In v1.5 mode, the canonical command path is nself config trust ssl.
The shorter spellings below remain compatibility aliases. The ACME client is
the default for setup, add and renew; --acme is only needed to select it in
v1.4 mode.
Use nself ssl setup to provision wildcard certificates via DNS-01 challenge for the configured base domain. Use nself ssl add to provision a certificate for a single external custom domain and generate the corresponding nginx server block automatically.
Certificates written by ssl setup and ssl add land in ssl/certificates/{domain-safe}/ inside the project directory (dots and colons in the domain are replaced with dashes, e.g. custom.example.com -> custom-example-com), which nginx reads via its ./ssl:/etc/nginx/ssl:ro volume mount.
Provisions SSL certificates with DNS-01 validation. In v1.5 mode (NSELF_V15=1) the CLI's own ACME client issues them and --install-cron installs the nself-acme-renew timer; in v1.4 mode certbot does (pass --acme to use the ACME client there too). Supports wildcard certificates for *.domain.
nself ssl setup [flags]
| Flag | Default | Description |
|---|---|---|
--provider |
cloudflare |
DNS provider (cloudflare, route53, digitalocean) |
--wildcard |
false |
Request a wildcard certificate (*.domain) |
--email |
(from ADMIN_EMAIL) |
Email address for Let's Encrypt registration |
--staging |
false |
Use the Let's Encrypt staging environment |
--install-cron |
false |
Install a systemd timer for automatic renewal (Linux only) |
# Wildcard certificate via Cloudflare DNS
nself ssl setup --provider cloudflare --wildcard
# Single-domain certificate via Route53
nself ssl setup --provider route53 --domain api.example.com
# Staging run (does not consume rate-limit quota)
nself ssl setup --provider cloudflare --stagingProvisions an SSL certificate for a single custom domain via HTTP-01 challenge (no DNS provider needed). In v1.4 mode certbot issues the certificate; in v1.5 mode the ACME client does. After the certificate is installed, writes an nginx server block to nginx/conf.d/custom-{domain}.conf and reloads nginx.
Certificates are stored in ssl/certificates/{domain-safe}/ (domain with dots/colons replaced by dashes) so nginx can read them at /etc/nginx/ssl/certificates/{domain-safe}/ inside the container.
nself ssl add <domain> [flags]
| Flag | Default | Description |
|---|---|---|
--upstream |
(none) | Backend service to proxy to (host:port). When omitted, a 200 placeholder response is returned until an upstream is configured. |
--acme |
off | Select the CLI's own ACME client over HTTP-01 in v1.4 mode (already the default in v1.5). Uses a pinned lego container and challenge files in ssl/.acme-webroot/. No DNS credential, age or age key is needed. |
--dry-run |
off | ACME client: print the resolved stack, lineage, target and webroot; write nothing. |
--nginx-container <name> |
(auto) | ACME client: the nginx container to reload. Default: the running nginx service of the served stack. |
--agree-tos |
off | ACME client: accept the ACME CA's terms of service when the account is first registered. Without it a terminal asks; a non-terminal run refuses. |
# Add certificate with proxy to an app container on port 3000
nself ssl add custom.example.com --upstream app:3000
# Add certificate without upstream (returns 200 placeholder)
nself ssl add custom.example.comnself ssl add custom.example.com --acme --agree-tos --upstream app:3000
With `--acme` the certificate is recorded as an `http-01` lineage in `ssl/.acme/lineages.json` and renewed by `nself ssl renew --acme` (also through the renewal timer). Before it asks the CA, the CLI writes a probe token into the webroot and fetches it from the served nginx over local HTTP; if nginx does not answer, nothing is issued and the error says to run `nself build` and restart nginx. The challenge location (bare tokens only, GET, no sub-paths, no symlinks) is in the default server, every non-SSL route block and the port-80 block of the custom conf; see [[Guide-SSL-Setup]].
ACME add refuses before issuance while a saved certbot hook writes into the served or project `ssl/` directory, including with `--dry-run`. Remove the hook lines or deploy scripts named in the error, then retry. Before issuing and installing a certificate, it disables and stops `nself-ssl-renew.timer` and rechecks the hooks so certbot cannot overwrite the ACME generation. If retirement fails, no certificate is issued or installed; the error names the required manual step. A dry run only reports whether the timer would be disabled. Renewal then uses the ACME timer installed by `ssl setup --acme --install-cron`, or `ssl renew --acme` run manually.
The generated conf file (`nginx/conf.d/custom-custom-example-com.conf`) includes:
- HTTP-to-HTTPS redirect on port 80
- TLS on port 443 with HTTP/2
- Security headers: `X-Frame-Options`, `X-Content-Type-Options`, `Referrer-Policy`, `Strict-Transport-Security`
- `proxy_pass` block (when `--upstream` is set) or placeholder `return 200`
## ACME setup and renewal
The CLI's own ACME client (DNS-01) issues, adopts and renews the certificates nginx serves, without host certbot. It runs a pinned `lego` container once per issuance through the docker funnel (no Docker socket). In v1.5 mode (`NSELF_V15=1`) `ssl setup`, `ssl add` and `ssl renew` use this client without `--acme`, and an ACME failure carries E470 (issuance), E471 (install or served-certificate check) or E472 (DNS credential missing) instead of E151. In v1.4 mode the certbot flows above are unchanged and `--acme` is additive.
In v1.5 mode:
nself config trust ssl setup [--provider cloudflare|route53|digitalocean] [--wildcard] [--email ] [--agree-tos] [--dry-run] [--install-cron] nself config trust ssl setup --adopt-certbot[=
In v1.4 mode, use `nself trust ssl` and add `--acme` to these commands.
ACME renewal selects managed lineages with 30 days or fewer left, installs the
renewed certificates, reloads the serving proxy and verifies the served
certificate. An optional argument selects a lineage by name. With `--force`,
renew regardless of expiry. In v1.4 certbot mode, renew reloads nginx; a domain
argument also requests certbot renewal when certbot is installed.
Adoption follows a served conf that names a top-level symlink of the ssl dir
(for example `/etc/nginx/ssl/api-task-nself-org/` linking to
`certificates/api-task-nself-org`). The link is followed one hop and only when
it lands on `certificates/<name>` inside the ssl dir; the run prints a note
naming the link. A link is not followed, the note says why, and the lineage is
refused as before, when it is absolute but not under `/etc/nginx/ssl` (the path
nginx sees), contains `..`, chains through another link, points at a directory
that does not exist, resolves outside `certificates/`, or `certificates/` is
itself a symlink. Two lineages whose confs reach one `certificates/<name>`
(through links or directly) are refused even with `--lineage`. Nothing in
the ssl dir is rewritten by this step.
| Flag | Applies to | Description |
|------|-----------|-------------|
| `--acme` | setup, renew | Use the CLI's ACME client instead of certbot (the default in v1.5 mode). In v1.4 mode every flag below needs it. |
| `--dry-run` | setup, renew | Print the resolved served root, ssl dir, nginx dir, nginx container, ssl mount, lineages and targets, then stop. Nothing is written. |
| `--nginx-container <name>` | setup, renew | The nginx container to reload. Default: the running `nginx` service of the served stack. |
| `--agree-tos` | setup, renew | Accept the ACME CA's terms of service when the account is first registered. Without it a terminal asks; a non-terminal run refuses. |
| `--adopt-certbot[=<dir>]` | setup | Adopt the lineages of a certbot tree (default `/etc/letsencrypt`). Never writes certbot's files and never issues; refuses while a certbot hook copies into the served or project `ssl/`, and disables `nself-ssl-renew.timer` afterwards. |
| `--dns-credential-file <file>` | setup | A certbot DNS-plugin INI. Its token is stored in the project secret store by name. |
| `--challenge dns-01\|http-01` | setup | Challenge for converted lineages. A `webroot` lineage with no credential file is adopted as `http-01` when the served nginx answers the challenge location for every name, and refused with a remediation otherwise; the other lineages convert to `dns-01`. |
| `--lineage <name>` | setup | Adopt only this certbot lineage. |
| `--force` | renew | Renew every lineage, not only those with 30 days or fewer left. |
| `--staging` | renew | Issue from the CA's staging directory into `.acme/staging/`. Installs nothing. |
| `--email <addr>` | renew | ACME contact. Default: the stored contact, then `ACME_EMAIL`, then `ADMIN_EMAIL`. |
| `--quiet` | renew | Suppress non-error output (the renewal timer uses it). |
| `--install-cron` | setup | Install `nself-acme-renew.service` and `.timer` (daily 03:30, Linux and systemd). With existing lineages it only installs the units; with `--dry-run` it prints them. First disables the certbot `nself-ssl-renew.timer` if it is enabled or running (refuses with the manual `systemctl` command if it cannot). |
`--dry-run` on `--acme` commands, run on a prod-shaped project (`nself-web/backend`, served by nself-web's nginx):
served root: /opt/nself-web served ssl dir: /opt/nself-web/ssl served nginx dir: /opt/nself-web/nginx nginx container: nself-web-nginx-1 nginx ssl mount: /opt/nself-web/ssl -> /etc/nginx/ssl (whole dir) acme contact: ops@example.org age: /usr/bin/age (key /home/deploy/.config/nself/age-key.txt) lineage api-task-nself-org: dns-01 via cloudflare domains: api.task.nself.org target: certificates/api-task-nself-org [directory, becomes a generation link on install] dry run: nothing written
`nself ssl status` prints a Lineages table (lineage, challenge, provider, expiry, days, status, targets) when `ssl/.acme/lineages.json` exists, and a warning when `/etc/letsencrypt/renewal` also manages one of a lineage's names. A second `--acme` run that starts while another holds `ssl/.acme/.lock` waits up to 10 seconds, then exits with `E151` naming the holder.
In v1.4 mode, ACME refusals use error `E151` with a remediation. In v1.5 mode,
classified failures use `E470`, `E471` or `E472` as described above; unclassified
failures and other preflight refusals retain `E151`. Refusals include an nginx
container that mounts single certificates instead of the whole ssl dir, a mount
of `/etc/nginx/ssl/...` that shadows the whole-dir mount, a target that is not
`certificates/<name>`, a non-ASCII name, a missing `age` or age key, no contact
email, a missing DNS credential, a credential file with no provider, two
providers or a Cloudflare global API key, or an unsupported DNS plugin.
<!-- END PROSE:description -->
## Flags
<!-- BEGIN GENERATED:flags -->
| Flag | Default | Description |
|------|---------|-------------|
| `--help`, `-h` | — | Show help |
<!-- END GENERATED:flags -->
## Subcommands
Use `add` to issue a certificate for one domain, `setup` for DNS-01
provisioning or adoption, `renew` for renewal, and `status` to inspect
certificates. See [[Command-Registry]] for the generated command reference,
or run `nself config trust ssl --help` for the current subcommand list.
## Examples
<!-- BEGIN PROSE:examples -->
```bash
# Check certificate status and expiry
nself ssl status
# Force certificate regeneration
nself ssl renew
# Provision wildcard via Cloudflare
nself ssl setup --provider cloudflare --wildcard --email admin@example.com
# Add custom domain with backend proxy
nself ssl add portal.example.com --upstream portal-app:8080
Sample status output:
Certificate: ssl/cert.pem
Issued to: *.localhost, localhost
Expires: 2027-03-28 (730 days remaining)
CA trust: trusted (mkcert CA installed)
SANs: localhost, *.localhost, api.localhost, auth.localhost
- Guide-SSL-Setup, full SSL setup walkthrough
- Config-Nginx, nginx configuration reference
- Guide-Production-Deployment, production server setup
Getting Started
Commands
- Commands, Overview
- Lifecycle: cmd-init · cmd-build · cmd-start · cmd-stop · cmd-restart · cmd-dev
- Monitoring: cmd-status · cmd-logs · cmd-health · cmd-urls · cmd-doctor · cmd-monitor · cmd-alerts · cmd-sentry · cmd-watchdog
- Data: cmd-db · cmd-backup · cmd-dr · cmd-queue · cmd-webhooks
- Config: cmd-config · cmd-service · cmd-env · cmd-promote
- Networking: cmd-ssl · cmd-trust · cmd-dns-setup
- Security: cmd-access · cmd-security · cmd-secrets
- Tenancy: cmd-tenant · cmd-billing
- Plugins: cmd-plugin · cmd-license · cmd-dogfood (extracted, CLI-R11) · cmd-k8s (extracted, CLI-R11) · cmd-encryption (extracted, CLI-R11) · cmd-waf (extracted, CLI-R11) · cmd-federation (extracted, CLI-R11) · cmd-mail (extracted, CLI-R11) · cmd-dlq (extracted, CLI-R11)
- AI: cmd-ai · cmd-claw · cmd-model
- Templates: cmd-template
- Utilities: cmd-exec · cmd-clean · cmd-reset · cmd-update · cmd-upgrade · cmd-version · cmd-admin · cmd-migrate · cmd-migrate-firebase · cmd-migrate-supabase · cmd-completion
Features
- Features, Overview
- Feature-Auth
- Feature-Storage
- Feature-Search
- Feature-Functions
- Feature-Email
- Feature-Monitoring
- Feature-Plugins
- Feature-nClaw, AI Assistant
- Feature-nChat, Messaging
- Feature-nFamily, Family Social
- Feature-nCloud, Managed Hosting
- Feature-Memory-Rooms, Knowledge Organization
- Feature-Agent-Dashboard, Agent Metrics
- Feature-Image-Generation, AI Image Generation
Configuration
- Configuration, Overview
- Config-Env-Vars
- Config-Postgres
- Config-Hasura
- Config-Auth
- Config-Nginx
- Shared-Proxy
- Config-Optional-Services
- Config-Custom-Services
- Config-System
Plugins (87 + 10 monitoring)
- Plugin-Overview
- Plugin-Catalog
- Plugin-Install
- Plugin-Licensing
- Plugin-Architecture
- Plugin-Dev-Guide
- Plugin-Migrations-Contract
Free (25)
- plugin-backup
- plugin-content-acquisition
- plugin-content-progress
- plugin-cron
- plugin-donorbox
- plugin-feature-flags
- plugin-github
- plugin-github-runner
- plugin-invitations
- plugin-jobs
- plugin-link-preview
- plugin-mdns
- plugin-mlflow
- plugin-monitoring
- plugin-notifications
- plugin-notify
- plugin-paypal
- plugin-search
- plugin-shopify
- plugin-stripe
- plugin-subtitle-manager
- plugin-tokens
- plugin-torrent-manager
- plugin-vpn
- plugin-webhooks
Pro (62)
- plugin-access-controls
- plugin-activity-feed
- plugin-admin-api
- plugin-analytics
- plugin-auth
- plugin-bots
- plugin-browser
- plugin-calendar
- plugin-cdn
- plugin-chat
- plugin-claw
- plugin-claw-budget
- plugin-claw-news
- plugin-claw-web
- plugin-cloudflare
- plugin-cms
- plugin-compliance
- plugin-ddns
- plugin-devices
- plugin-documents
- plugin-entitlements
- plugin-file-processing
- plugin-game-metadata
- plugin-geocoding
- plugin-geolocation
- plugin-google
- plugin-home
- plugin-idme
- plugin-knowledge-base
- plugin-linkedin
- plugin-livekit
- plugin-media-processing
- plugin-meetings
- plugin-moderation
- plugin-mux
- plugin-object-storage
- plugin-observability
- plugin-photos
- plugin-podcast
- plugin-post
- plugin-realtime
- plugin-retro-gaming
- plugin-rom-discovery
- plugin-social
- plugin-sports
- plugin-support
- plugin-tmdb
- plugin-voice
- plugin-web3
- plugin-workflows
Planned (26)
plugin-auditplugin-blogplugin-checkoutplugin-commerceplugin-drmplugin-exportplugin-flowplugin-importplugin-ldapplugin-mailgunplugin-mediaplugin-oauth-providersplugin-pagesplugin-postmarkplugin-rate-limitplugin-reportsplugin-samlplugin-schedulerplugin-sendgridplugin-ssoplugin-subscriptionplugin-thumbplugin-transcoderplugin-twilioplugin-wafplugin-watermark
Guides
- Guide-Production-Deployment
- Guide-SSL-Setup
- Guide-Multi-Tenancy
- Guide-Security-Hardening
- Guide-Monitoring-Setup
- Guide-Backup-Restore
- Guide-Custom-Services
- Guide-Migration-from-v1
Architecture
- Architecture
- Architecture-Guardrails
- Service-Graph
- Compose-Generation
- Nginx-Generation
- Plugin-Architecture
Reference
Machine interface
-
JSON-Output,
--jsonenvelope -
Command-Registry,
nself help --json - Exit-Codes
- error-codes, Error Codes
-
Compat-V15,
NSELF_V15gate
Licensing
Security
Brand
Operations
- operations/release-cascade, Release Cascade
- operations/self-healing, Self-Healing Schema
- operations/redis-tuning, Redis Pool Tuning
- operations/meilisearch-warmup, MeiliSearch Warm-Up
- operations/jwt-rotation, JWT Key Rotation
- operations/windows-wsl2-setup, Windows / WSL2 Setup
- operations/gemini-oauth-reauth, Gemini OAuth Reauth
Contributing
Admin
- USER-ACTION-QUEUE, Pending Admin Actions
All commands (21)
- A: cmd-add · cmd-admin
- B: cmd-backup · cmd-build
- C: cmd-clean · cmd-config
- D: cmd-db · cmd-deploy · cmd-doctor
- E: cmd-exec
- I: cmd-init
- L: cmd-license · cmd-logs
- M: cmd-mcp
- R: cmd-remove · cmd-reset · cmd-restart
- S: cmd-start · cmd-status · cmd-stop
- U: cmd-update