TLS / HTTPS
NORA serves plain HTTP by design. TLS termination is handled by a reverse proxy.
Why no built-in TLS?
Section titled “Why no built-in TLS?”This is a deliberate architectural decision:
- Single responsibility — NORA manages artifacts, not certificates. Embedding TLS means bundling Let’s Encrypt clients, certificate renewal, ACME challenges, and custom CA support — all of which already exist in battle-tested tools.
- Operational simplicity — One place for certificates (reverse proxy), not scattered across every service. When a cert expires, you fix it in one config.
- Industry standard — Docker Hub, GitHub Container Registry, AWS ECR, Harbor, Nexus — none terminate TLS in the registry process. A reverse proxy in front is the universal pattern.
- Zero-config on internal networks — On trusted networks (lab, CI/CD), NORA works out of the box without certificate management.
Production: Reverse Proxy with TLS
Section titled “Production: Reverse Proxy with TLS”Architecture
Section titled “Architecture”Client (docker push) → Reverse Proxy (HTTPS :443) → NORA (HTTP :4000)Caddy (recommended — auto Let’s Encrypt)
Section titled “Caddy (recommended — auto Let’s Encrypt)”registry.example.com { reverse_proxy localhost:4000}That’s it. Caddy handles certificate issuance and renewal automatically.
server { listen 443 ssl; server_name registry.example.com;
ssl_certificate /etc/letsencrypt/live/registry.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/registry.example.com/privkey.pem;
# Required for large image pushes client_max_body_size 0;
location / { proxy_pass http://127.0.0.1:4000; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; }}Traefik
Section titled “Traefik”services: traefik: image: traefik:v3 command: - --entrypoints.websecure.address=:443 - --certificatesresolvers.le.acme.tlschallenge=true - --certificatesresolvers.le.acme.email=admin@example.com ports: - 443:443
nora: image: ghcr.io/getnora-io/nora:latest labels: - traefik.http.routers.nora.rule=Host(\`registry.example.com\`) - traefik.http.routers.nora.tls.certresolver=le - traefik.http.services.nora.loadbalancer.server.port=4000Internal Network: Insecure Registry
Section titled “Internal Network: Insecure Registry”If you run NORA without TLS on a private network, configure Docker clients to trust it.
Docker daemon configuration
Section titled “Docker daemon configuration”Edit /etc/docker/daemon.json on every Docker client that needs to push/pull:
{ insecure-registries: [192.168.1.100:4000]}Then restart Docker:
sudo systemctl restart dockerDNS name
Section titled “DNS name”If you use a DNS name (e.g. nora.internal:4000), add that instead:
{ insecure-registries: [nora.internal:4000]}Warning:
insecure-registriesdisables TLS certificate verification for that host. Use only on trusted networks where traffic between client and registry is not exposed.
Kubernetes (containerd)
Section titled “Kubernetes (containerd)”For K8s nodes using containerd, edit /etc/containerd/config.toml:
[plugins.io.containerd.grpc.v1.cri.registry.configs.nora.internal:4000.tls] insecure_skip_verify = trueThen restart containerd:
sudo systemctl restart containerdCustom CA Certificates
Section titled “Custom CA Certificates”If your organization uses a private CA (e.g., FreeIPA, internal PKI):
Docker
Section titled “Docker”# Copy CA certsudo mkdir -p /etc/docker/certs.d/registry.example.com:4000/sudo cp ca.crt /etc/docker/certs.d/registry.example.com:4000/ca.crt
# No Docker restart neededSystem-wide (Ubuntu/Debian)
Section titled “System-wide (Ubuntu/Debian)”sudo cp ca.crt /usr/local/share/ca-certificates/my-ca.crtsudo update-ca-certificatesSystem-wide (RHEL/CentOS/Astra/RedOS)
Section titled “System-wide (RHEL/CentOS/Astra/RedOS)”sudo cp ca.crt /etc/pki/ca-trust/source/anchors/my-ca.crtsudo update-ca-trustSee Also
Section titled “See Also”- Settings — all configuration options
- Authentication — htpasswd, API tokens, RBAC
- Production Guide — full deployment guide