Container Registry
DanubeData operates a managed OCI registry at cr.danubedata.ro that your CI/CD can push images to and your Rapids can pull from — no third-party credentials required for your own team's images. Storage is backed by our object-storage tier; pulls from inside the platform stay on the cluster LAN (free).
What you get
- A registry namespace scoped to your team, hosted at
cr.danubedata.ro/{your-team-slug}/.... - Token-only authentication. Same flow as DigitalOcean: you generate one token, use it as the Docker password, and put any string (typically your account email) in the username field. We identify the key by hashing the token — the username is informational only.
- Short-lived (5 min) JWTs under the hood, so a revoked key stops working within minutes.
- Auto-wired Rapid integration — your team's first-party images appear under "DanubeData Container Registry" in the Rapid credential dropdown without any setup.
- A free Starter plan (1 repository, 500 MB) on every team by default; paid plans for more repositories and storage.
Your team slug is the same tenant_name shown in Team Settings → General. The rest of this page assumes your team slug is acme and your account email is you@example.com.
Create an access key
- Go to Container Registry and choose New access key at the top of the page.
- Enter a Name (e.g.
github-actions-prod). - Pick the Access:
- Push and pull — for CI pipelines that build and publish images.
- Pull only — for read-only consumers (deploy targets, mirrors).
- Click Create access key, then confirm it's you when asked.
We email you whenever a registry access key is created.
The Token is shown once. Copy or download it now — we never display it again. The token is stored hashed in our database; even root access to the database does not expose it.
Tokens look like cr_aBcD12ef... — the cr_ prefix lets credential scanners (git-secrets, trufflehog) and humans spot them on sight in .env files, CI logs, or ~/.docker/config.json.
You can create as many keys as you want. To revoke one, open it under Container Registry → Access keys and choose Revoke; the next docker push against the revoked key fails within 5 minutes. Only the team owner creates, sees and revokes access keys.
No more "username" to copy. Earlier keys had an auto-generated username like
dd-acme-github-actions-prod-3f9k2a. New keys still have one for back-compat (visible under the "Show the auto-generated username" disclosure in the reveal modal), but you don't need it — any value works in the-ufield. Your account email is the recommended default.
Log in from Docker
Use your account email as the username — Docker requires something in -u, and your email is memorable and not secret. The token is the password.
echo "$REGISTRY_TOKEN" | docker login cr.danubedata.ro \
-u you@example.com --password-stdin
For interactive use:
docker login cr.danubedata.ro -u you@example.com
# Password: (paste the cr_... token)
Any string works for the username, so this is also valid:
echo "$REGISTRY_TOKEN" | docker login cr.danubedata.ro -u _token --password-stdin
Push an image
Tag the image under your team slug and push:
docker build -t cr.danubedata.ro/acme/hello:1.0.0 .
docker push cr.danubedata.ro/acme/hello:1.0.0
The first path segment must be your team slug (acme in the example). Pushes to any other namespace are rejected with denied: requested access to the resource is denied.
You can organise your images however you like under that prefix:
cr.danubedata.ro/acme/web:1.0.0
cr.danubedata.ro/acme/web:latest
cr.danubedata.ro/acme/worker:1.0.0
cr.danubedata.ro/acme/staging/web:abc1234
Use the image in a Rapid
- In the Rapid Create or Edit form, set Deployment type to Docker image.
- Image:
cr.danubedata.ro/acme/hello:1.0.0 - Registry credential: pick DanubeData Container Registry from the dropdown — it's pre-seeded for every team and authenticates against your first-party registry.
- Save / deploy. Knative pulls the image using a short-lived pull-only credential the platform manages for you.
You do not need to create or rotate any credentials for first-party pulls. The platform handles that.
Push from GitHub Actions
name: build-and-push
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Log in to DanubeData
uses: docker/login-action@v3
with:
registry: cr.danubedata.ro
username: ${{ secrets.DD_REGISTRY_USERNAME }} # your email — not secret
password: ${{ secrets.DD_REGISTRY_TOKEN }} # the cr_... token
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
cr.danubedata.ro/acme/hello:${{ github.sha }}
cr.danubedata.ro/acme/hello:latest
Add DD_REGISTRY_USERNAME (your account email or any string) and DD_REGISTRY_TOKEN (the cr_... token) under Settings → Secrets and variables → Actions in your GitHub repo. Only the token needs to be a secret; the username can also live in plain vars.
Push from GitLab CI
build:
image: docker:24
services:
- docker:24-dind
stage: build
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- echo "$DD_REGISTRY_TOKEN" | docker login cr.danubedata.ro -u "$DD_REGISTRY_USERNAME" --password-stdin
script:
- docker build -t cr.danubedata.ro/acme/hello:$CI_COMMIT_SHORT_SHA .
- docker push cr.danubedata.ro/acme/hello:$CI_COMMIT_SHORT_SHA
Define DD_REGISTRY_USERNAME (your account email) and DD_REGISTRY_TOKEN (the cr_... token) under Settings → CI/CD → Variables (mark the token as Masked and Protected).
Push from Bitbucket Pipelines
image: atlassian/default-image:4
pipelines:
branches:
main:
- step:
name: Build and push
services: [docker]
script:
- echo "$DD_REGISTRY_TOKEN" | docker login cr.danubedata.ro -u "$DD_REGISTRY_USERNAME" --password-stdin
- docker build -t cr.danubedata.ro/acme/hello:$BITBUCKET_COMMIT .
- docker push cr.danubedata.ro/acme/hello:$BITBUCKET_COMMIT
Define DD_REGISTRY_USERNAME (your account email) and DD_REGISTRY_TOKEN (the cr_... token) under Repository settings → Repository variables (mark the token as Secured).
Tag conventions
- Use immutable, sortable tags (commit SHA, semver) for production Rapids — Kubernetes will not re-pull
:latestonce a pod has cached it. :latestis fine for development. For production we recommend pinning to a SHA or a semver tag.- Tag names must match
[a-z0-9]+(?:[._-][a-z0-9]+)*per path segment (Distribution's standard rule). Uppercase letters are not allowed in the registry path; they are allowed in tags. - We do not currently overwrite-protect tags. Pushing the same tag twice replaces the manifest. Future plans include "immutable tag" toggles.
Delete images
From the Container Registry UI:
- Delete a single image — open the repository (Container Registry → Repositories → {repo}), click the image and choose Delete in the panel that opens. The repository stays.
- Delete multiple images — on the repository's page, tick the images you want to remove and click Delete above the list. Images a Rapid runs are left out, and the confirmation names them. The repository stays even if all its images are deleted.
- Delete the whole repository — on the repository's page, open More and pick Delete repository. All its images are removed and the repository drops from your dashboard.
A delete removes the image, not only the tag you chose: other tags that point at the same image (say latest and 1.4.0) go with it. The console lists them before you confirm. Deleting is for the team owner.
In all three cases the underlying blob storage is reclaimed at the next garbage-collection run (daily). Tag and repository deletions are not recoverable.
Images a Rapid runs are protected. A tag that one of your serverless containers was deployed from — or any tag sharing the same image — cannot be deleted from the dashboard, the API or the CLI. Knative pins that exact image into the container's revision, so removing it would break the container's next cold start. The refusal names the container: redeploy it on another tag or remove it first. When you really mean it, the API accepts force=true and the CLI --delete-in-use.
Plans & limits
| Plan | Repositories | Storage | Price |
|---|---|---|---|
| Starter | 1 | 500 MB | Free |
| Basic | 5 | 5 GB | EUR 3.99/mo |
| Professional | unlimited | 100 GB | EUR 14.99/mo |
| Enterprise | custom | custom | Contact us |
Change plan under Container Registry → Plan and usage → Change plan. A switch takes effect immediately and is billed at the new plan's rate from then on. A downgrade to a plan smaller than what you store goes through, but puts your account on hold: pushes are refused, and so is creating any new resource, until support lifts the hold. Delete images first to switch without one; the console says so before you confirm.
Troubleshooting
unauthorized: authentication required
Your token is wrong, revoked, or expired.
# Verify the token directly (username can be anything — try your email):
curl -i -u "you@example.com:$DD_REGISTRY_TOKEN" \
"https://danubedata.ro/oci/token?service=cr.danubedata.ro&scope=repository:acme/hello:pull"
A 200 OK with a JSON {"token":"...", ...} body means your credentials are valid. A 401 means the token is wrong, revoked, or expired — re-issue from the dashboard.
denied: Storage quota exceeded (X MB used of Y MB). Upgrade your plan or delete tags to free space.
You are at your plan's storage cap. Either upgrade under Container Registry → Plan and usage, or delete unused tags/repositories to reclaim quota. The next push succeeds immediately after you free space — actual disk bytes are reclaimed at the nightly GC, but the quota check uses your live bytes_used total which drops as soon as the tag is deleted.
denied: Repository limit reached (X of X). Upgrade your plan or delete an existing repository to add a new one.
You are trying to push to a new repository path while at your plan's repo cap. Either upgrade, or delete an existing repository under Container Registry → Repositories. Pushing additional tags to a repository you already own is unaffected.
denied: requested access to the resource is denied
The first path segment of your image does not match your team slug. Tag the image as cr.danubedata.ro/{your-team-slug}/... and push again.
ImagePullBackOff on a Rapid
Most common causes, in order:
- The image tag does not exist (typo, push not yet finished). Check Container Registry → Repositories → {repo} to confirm the tag is listed.
- The Rapid is configured to pull from a different team's image. First-party pulls only work when the team that owns the Rapid matches the team slug in the image path.
- The auto-seeded internal credential is stale (e.g. you renamed the team). Trigger a re-deploy of the Rapid; the platform re-materialises the pull secret.
The Rapid detail page surfaces the underlying Kubernetes event — it will tell you exactly which of these is in play.
Push hangs at the last layer / "retrying in N seconds"
Likely a transient network issue or you are pushing during the nightly GC window. Docker retries automatically; if it has not resolved after two minutes, contact support.
Cross-references
If you need to push from or pull from a third-party registry (GHCR, Docker Hub, GitLab Container Registry, ECR, etc.) into a Rapid, see Private Container Registry.