# Container registry

Codebahn ships a built-in OCI container registry. Build an image in CI and push it to `codebahn.net/<owner>/<image>`, then pull it from a deploy target, a Kubernetes cluster, or another workflow. Images live on EU object storage; all data stays within EU infrastructure.

To publish images from CI, follow [Publish container images](/docs/publish-container-images/). To pull them, follow [Consume container images](/docs/consume-container-images/). This page is the reference.

## Private by default

Every image is private. There is no public or anonymous pull, on any plan. A pull always needs a credential that can read the owning user or org. This matches the rest of Codebahn: tenant data is never browsable without auth.

The registry is the most-used surface and the one we support directly. Codebahn also exposes other package formats (npm, Maven, NuGet, RubyGems, and more) through Forgejo's package API; those are available and documented upstream. See the [Forgejo packages docs](https://forgejo.org/docs/latest/user/packages/) for their usage.

## Image names and host

| Part | Value |
| --- | --- |
| Registry host | `codebahn.net` |
| Image path | `codebahn.net/<owner>/<image>` |
| Owner | a user or org you belong to |

The host carries a dot, so Docker treats it as a registry rather than a Docker Hub shortname. `codebahn.net/acme/api:1.4.0` is an org image; `codebahn.net/alice/tool:latest` is a user image.

## Authentication

In Codebahn CI, a job authenticates to its own owner's registry with the built-in `${{ github.token }}`, so there is nothing to create or store. It can push and pull its own owner's images; a fork pull request gets neither. See [Publish container images](/docs/publish-container-images/) for the workflow.

Everywhere else (a workstation, an external CI system, a deploy target, or pushing across owners) log in with a username and a [personal access token](https://forgejo.org/docs/latest/user/api-usage/#authentication) (PAT), not your password. Scope the token to what it needs:

| Task | Token scope |
| --- | --- |
| Pull | `read:package` |
| Push | `write:package` |
| Delete a version | `delete:package` |

```bash
echo "$CODEBAHN_TOKEN" | docker login codebahn.net -u "$USERNAME" --password-stdin
```

`--password-stdin` keeps the token out of your shell history and the process list. For an unattended consumer or publisher, use a dedicated machine user rather than a personal account; see [Publish container images](/docs/publish-container-images/#push-from-outside-codebahn-ci).

## Storage and quota

Image layers count against your plan's storage quota, the same pool as LFS, CI artifacts, and attachments (objects storage). See [Limits](/docs/limits/) for the caps.

Two things to know about how container storage is counted:

- **Layers are stored once but counted per reference.** Object storage deduplicates identical layers by content hash, so pushing the same base layer to five tags stores it once. Quota counts each reference, so that layer counts five times against your quota. Reuse tags and prune old ones to keep the number down.
- **Untagged manifests are pruned automatically.** A daily cleanup removes manifests and blobs left unreferenced for more than 24 hours, so overwriting `:latest` does not leak storage forever. You can also set per-owner cleanup rules in the package settings; see the [Forgejo cleanup rules docs](https://forgejo.org/docs/latest/user/packages/#cleanup-rules).

## Where the bytes live

Blobs are stored on Scaleway Object Storage in the EU (`fr-par`), encrypted at rest. Pulls are served directly from object storage over a short-lived signed link after your credential is checked, so large images do not bottleneck on the application server. All image data stays in the EU, served through EU-only infrastructure.
