- Documentation
- Services
- Private Container Registries
Private Container Registries
When you deploy a service from a pre-built Docker image, Guara Cloud can pull from any private container registry — Docker Hub private repos, GitHub Container Registry, Google Container Registry, Quay.io, GitLab, self-hosted Harbor, and any V2-compatible registry.
You attach the credentials directly to the service. There is no global “registry credentials” tab and no need to bake tokens into your image or pass them through environment variables.
Where to find it
There are two entry points, depending on whether you’re creating a new service or editing an existing one:
- Service create wizard. When you choose Docker Image as the build method and provide a private image reference, a collapsed Private Registry Credentials section appears below the image reference field.
- Service Settings tab. Open the service, go to Settings, and find the Private Registry section. If credentials are already set, the section is expanded with the current values; otherwise it shows a single ”+ Add private registry credentials” button.
Both surfaces share the same form and validation flow.
Adding credentials
-
Registry URL. The registry hostname or full URL (e.g.,
gcr.io,ghcr.io,quay.io,docker.io, or your self-hosted host). -
Username. The username for basic auth, or the token username for token-based auth (commonly
oauth2accesstokenfor GCR,_json_key_base64for GCR JSON keys, etc.). -
Password. The password or token. The plaintext value never leaves the request — see the security section below.
-
Test Connection. Click the button. The platform performs a real preflight against your registry.
-
Save. Once the test passes, the Save button is enabled.
Test Connection
The Test Connection button runs a real preflight against the registry — it doesn’t just check that the URL parses. The flow:
- Probe the registry’s V2 API endpoint (
GET /v2/) to confirm it speaks the OCI protocol. - Exchange auth tokens if the registry challenges with
WWW-Authenticate: Bearer realm=.... - Send a HEAD request for the manifest of the image reference you provided to confirm it actually exists in this registry under these credentials.
The whole preflight has an 8-second wall-clock timeout. Failures map to friendly codes:
| Code | Meaning |
|---|---|
invalid_url | The URL is malformed or the host is unreachable. |
auth_failed | The username or password were rejected by the registry. |
manifest_not_found | The image reference does not exist in this registry. |
timeout | The registry took longer than 8 seconds to respond. |
ssrf_blocked | The registry hostname resolves to a blocked address (loopback, RFC 1918, etc.). |
network_error | Some other network failure (DNS, TLS, etc.). |
Test Connection is rate-limited to 5 attempts per minute per user. If you hit the limit, the dashboard shows a soft message and you can try again shortly.
Save without verifying
If the test fails but you’re confident the credentials are correct (for example, the registry is briefly down for maintenance), check Save without verifying to skip the preflight requirement and persist the credentials anyway.
Updating and removing
- Edit. Click the field group to reveal the form. Username and password are masked; you can replace them or leave them as-is. Re-test before saving.
- Remove. Click Remove credentials. The service falls back to anonymous pulls. If your image is private, the next deploy will fail until credentials are added back.
Security
- Passwords are encrypted at rest with AES-256-GCM, using a per-project key derived via HKDF from the platform’s master key. Each project has its own derived key — no shared secret across projects.
- The plaintext password is held only in memory during the request that creates or updates credentials, the preflight HTTP call, and the encrypt-and-persist transaction. It is never logged, cached, or written to disk.
- The registry preflight is bound by SSRF protection — the same blocklist that protects HTTP cron worker destinations. Loopback, RFC 1918, link-local, carrier-grade NAT, IPv4-mapped IPv6, and the unspecified address are all blocked.
- Every save is recorded in your Audit Logs under
service.createorservice.update, with metadata indicating whether the preflight passed or was overridden.
Supported registries
Anything that speaks the OCI Distribution Spec V2 works. Common examples:
| Registry | URL example | Username notes |
|---|---|---|
| Docker Hub (private repo) | docker.io | Your Docker Hub username |
| GitHub Container Registry | ghcr.io | Your GitHub username; password = personal access token |
| GitLab Container Registry | registry.gitlab.com | Your GitLab username; password = personal access token |
| Google Container Registry | gcr.io | oauth2accesstoken or _json_key_base64 |
| Quay.io | quay.io | Your Quay username |
| Self-hosted Harbor | harbor.example.com | The robot account or user |