# Container images

The quickest way to run something: any image from Docker Hub, GitHub Container Registry or another registry the servers can pull from.

## Create one

**+ Create → Service → Container image**, then enter the image, e.g. `nginx:1.27`, `postgres:16` or `ghcr.io/acme/api:2.3`.

- **Port**: the port the app listens on inside the container. Leave it empty and Alf uses the port the image declares (`EXPOSE`), or 8080. The app also gets a `PORT` variable.
- **Command**: optional; replaces the image's command, e.g. `redis-server --appendonly yes`.

## Updating

Change the image (say from `:1.2` to `:1.3`) under the service's **Settings**: that starts a deployment. To fetch a moving tag like `:latest` again, press **Deploy** on the service's page.

## Private registries

Give the organization a login for the registry, and its private images are pulled with it: services that run one, and builds whose Dockerfile starts `FROM` one.

```bash
alf registries add ghcr.io --username you
```

The token is asked for and not shown (or piped in: `echo "$GHCR_TOKEN" | alf registries add ghcr.io --username you`, for CI). In the dashboard: **Settings → Registries**. The registry is its host, as images from it start with: `ghcr.io`, `registry.gitlab.com`, `registry.example.com:5000`, or `docker.io` for Docker Hub. One login per registry; adding one again replaces it. Admins add and remove them.

| Registry | Username | Token |
| --- | --- | --- |
| GitHub (`ghcr.io`) | your GitHub username | a personal access token (classic) with `read:packages` |
| Docker Hub (`docker.io`) | your Docker ID | an access token, read-only |
| GitLab (`registry.gitlab.com`) | the token's username | a deploy token with `read_registry` |

- **It's checked first.** Alf asks the registry, as Docker does, and a login it turns away isn't kept. A registry that can't be reached from Alf is saved anyway, and the first pull says whether it works.
- **It stays the organization's.** The token is encrypted and never shown again. Servers get it only with the organization's own services, for their pulls and builds. An image pulled with it is kept under a name of the organization's own on that server, so another organization's service naming the same image doesn't get it: its server has to pull it, and the registry asks for a login.
- **New pulls use it.** After adding one, deploy the service again (or **Redeploy**). Removing one leaves images already pulled where they run.

A private image pulled without a login fails with `pull access denied` or `unauthorized`. The deployment says which registry needs a login.

`alf registries` lists the logins, and `alf registries remove ghcr.io` takes one away.
