Registry Reference
Jiji auto-detects namespace requirements for the registries below, so configuration stays minimal.
Supported Registries
GitHub Container Registry (GHCR)
builder:
registry:
server: ghcr.io
username: your-github-username
password: GITHUB_TOKENAuto namespace: username. Images push to
ghcr.io/your-github-username/project-service:version.
Create a token at github.com/settings/tokens
with write:packages and read:packages, then export GITHUB_TOKEN=ghp_... (or point password at that name and put it in your
.env).
Docker Hub
builder:
registry:
server: docker.io
username: your-dockerhub-username
password: DOCKER_PASSWORDAuto namespace: username. Images push to
docker.io/your-dockerhub-username/project-service:version.
Custom registry
builder:
registry:
server: registry.example.com:5000
username: myuser
password: REGISTRY_PASSWORDNo auto namespace. Images push to
registry.example.com:5000/project-service:version.
AWS ECR / GCP Artifact Registry
Set an explicit server, as with any custom registry. Both providers issue
short-lived credentials, so password:
uses the $(...) command-value syntax to fetch a fresh one on every run
(runs locally through a shell, uses the trimmed stdout, 30 second timeout)
instead of a stale value going into .env:
builder:
registry:
server: <account-id>.dkr.ecr.<region>.amazonaws.com
username: AWS
password: "$(aws ecr get-login-password --region <region>)"builder:
registry:
server: <region>-docker.pkg.dev
username: _json_key_base64
password: "$(base64 -w0 service-account-key.json)"Local registry
builder:
registry:
port: 31270 # optional, defaults to 31270No namespace, no credentials. Images are stored at
localhost:31270/project-service:version - see “Local Registry Details”
below for how it reaches remote servers.
Registry Password
password can be a literal value, an ALL_CAPS secret name resolved the
same way as everywhere else in config (from .env, or host environment
with --host-env), or $(a local command) - see “AWS ECR / GCP Artifact
Registry” above:
builder:
registry:
password: GITHUB_TOKENThe password is sent to the container engine over stdin only. It’s never placed in a command string, logged, or printed.
Registry Commands
jiji build and jiji deploy --build authenticate automatically:
remote registries locally before pushing, and on every selected deployment
host before pulling. Local registries need no credentials.
To authenticate or clear credentials explicitly:
jiji registry login
jiji registry login --skip-local # servers only
jiji registry login --skip-remote # local machine only
jiji registry logout
jiji registry logout --skip-local
jiji registry logout --skip-remoteBy default both commands act on the local machine, then every -H-selected
server (all configured servers if -H is omitted). jiji registry login
requires server, username, and password to be set.
jiji registry logout only needs server, and is idempotent - an engine
reporting “already logged out” counts as success. -S/--services is
rejected for both: registry credentials belong to a host’s container
engine, not an individual service. Both commands try every requested
target even if one fails, then exit nonzero if any target failed.
Remove the local registry container with:
jiji registry teardown
jiji registry teardown --dry-run
jiji registry teardown --yesTeardown verifies jiji’s ownership label and the configured port before
removing the jiji-registry container, and refuses to touch anything that
doesn’t match.
Local Registry Details
How it works
For a local build and deploy:
- Starts or reuses the loopback-bound
jiji-registrycontainer - Builds and pushes versioned images to
localhost:31270 - Opens SSH connections to selected deployment servers and creates
reverse tunnels (
remote 127.0.0.1:31270 -> local 127.0.0.1:31270) - Each server pulls the newly built image
- Tunnels are torn down after the deploy; the registry container itself keeps running for the next build
When builder.remote is set, there are two independent tunnel phases:
- Jiji binds
127.0.0.1:<registry.port>on the remote builder through SSH. Build and push commands on the builder use that loopback address to reach the registry on the operator machine. - The builder tunnel is cleaned up with the remote build session.
- During deployment, Jiji separately binds the configured port on each selected deployment server so its engine can pull the image.
The builder needs no inbound access to the operator machine, and the
registry is never publicly exposed. The SSH servers must permit remote
forwarding, but every forwarded listener remains loopback-only, so
GatewayPorts is not required.
The builder-side port is fixed. Concurrent remote builds using the same builder and local-registry port compete for that one loopback listener. Jiji reports the bind conflict and does not replace the existing listener or automatically select another port. Serialize those builds, or configure distinct registry ports for independent invocations.
The registry container is labeled as jiji-managed. If jiji-registry
already refers to something else, or its recorded port doesn’t match your
config, jiji stops and asks you to resolve the conflict rather than
guessing.
Advantages
- No external registry account needed for local development
- No push/pull round trip to a remote registry
- Works fully offline
- Traffic is encrypted over the SSH tunnel
Troubleshooting
A host can’t connect to localhost:<registry.port>:
# Local operator: confirm the registry is running
curl http://127.0.0.1:<registry.port>/v2/
# Remote builder, during a remote build
ss -tlnp | grep <registry.port>
# Deployment server, during a deployment
ss -tlnp | grep <registry.port>On the builder or deployment server, check that sshd_config allows remote
forwarding:
AllowTcpForwarding yesJiji binds the forwarded port to the remote host’s 127.0.0.1 only.
GatewayPorts is not required, and shouldn’t be enabled just for jiji -
it would expose the forwarded registry on non-loopback interfaces.
Remote Registry Authentication
Per-environment credentials
# jiji.staging.yml
builder:
registry:
server: ghcr.io
username: myorg
password: STAGING_GITHUB_TOKEN# jiji.production.yml
builder:
registry:
server: ghcr.io
username: myorg
password: PRODUCTION_GITHUB_TOKENCI/CD
# GitHub Actions example
- name: Deploy
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: jiji deploy -yNever commit .env files - add .env* to .gitignore, and keep separate
tokens per environment.
Troubleshooting
GHCR 403 Forbidden: confirm the token has write:packages, the
username matches the GitHub user/org, and the token can create packages
under that namespace.
“GHCR requires username to be configured”: add username to
builder.registry - GHCR and Docker Hub both need it for automatic
namespace detection.