Introduction
The Crusoe Container Registry (CCR) is a Docker-compliant private registry for storing and serving container images within Crusoe Cloud. Storing your images in a CCR repository co-located with your compute avoids egress charges from external registries, eliminates rate-limiting during large-scale Kubernetes image pulls, and reduces pod startup time.
This guide covers creating a repository, authenticating Docker, pushing your first image, pulling it on a VM, and using CCR with a Kubernetes cluster. It also explains how to set up a pull-through cache for external registries such as Docker Hub, NVIDIA NGC, or AWS ECR.
Prerequisites
- Active Crusoe Cloud account with billing enabled
- The Crusoe CLI installed and configured (see crusoe config init)
- Docker installed on your local machine or VM
- kubectl installed and configured if you are using a Kubernetes cluster (CMK or self-managed)
Instructions
Create a CCR Repository
- A repository is the container (think: folder) inside CCR where your images live. You can create one per team, application, or environment
- Create a standard read/write repository via the CLI:
crusoe registry repositories create \
--name my-app-repo \
--location us-east1-a \
--mode standard- Or create one via the console: navigate to Container Registry in the left nav, click Create Repository, select Standard mode, and click Create
- The command returns your repository URL. It follows this format:
registry.<location>.ccr.crusoecloudcompute.com/<repo-name>.<first-8-chars-of-project-id>- Save this URL — you will need it in every subsequent step
💡 Tip: Provision your repository in the same location as your compute (e.g., both in us-east1-a). This maximises pull speeds and avoids inter-region transfer.
Create an Authentication Token
- CCR uses token-based authentication. You will use this token as your Docker password
- Generate a token via the CLI:
crusoe registry tokens create --alias my-app-token- Or create one via the console: navigate to your repository, click Create Token, give it an alias, and click Create
⚠️ Important: The token is only shown once. Copy it immediately and store it securely (e.g., in a password manager or a Kubernetes Secret). You cannot retrieve it again.
Authenticate Docker to CCR
- Run docker login using your Crusoe account email as the username and the token from Step 2 as the password:
docker login registry.us-east1-a.ccr.crusoecloudcompute.com/my-app-repo.7hf6et43 \
-u your@email.com \
-p 'your-ccr-token-here'⚠️ Note: Always wrap your token in single quotes. CCR tokens can contain special characters (such as $) that your shell may try to expand as variables if you use double quotes.
- A successful login returns: Login Succeeded
Tag and Push an Image
- Docker images must be tagged with the full CCR repository URL before they can be pushed. The format is:
<ccr-repo-url>/<image-name>:<tag>- Example: pull a public image, tag it for CCR, and push it:
# Pull from Docker Hub
docker pull nvidia/cuda:12.4.1-base-ubuntu22.04
# Tag it for your CCR repository
docker tag nvidia/cuda:12.4.1-base-ubuntu22.04 \
registry.us-east1-a.ccr.crusoecloudcompute.com/my-app-repo.7hf6et43/cuda:12.4.1
# Push to CCR
docker push registry.us-east1-a.ccr.crusoecloudcompute.com/my-app-repo.7hf6et43/cuda:12.4.1- Or, if you are building a custom image from a Dockerfile, build and tag in one step:
docker build -t registry.us-east1-a.ccr.crusoecloudcompute.com/my-app-repo.7hf6et43/my-trainer:v1.0 .
- Verify the Push
- Confirm the image is listed in your repository:
crusoe registry images list my-app-repo --location us-east1-a- Or check via the console: navigate to Container Registry, click your repository, and confirm the image appears in the list
Pull the Image on a Crusoe VM
- SSH into your Crusoe VM and authenticate Docker using the same token:
docker login registry.us-east1-a.ccr.crusoecloudcompute.com/my-app-repo.7hf6et43 \
-u your@email.com \
-p 'your-ccr-token-here'- Then pull the image:
docker pull registry.us-east1-a.ccr.crusoecloudcompute.com/my-app-repo.7hf6et43/cuda:12.4.1💡 Tip: For automated workflows (CI/CD, startup scripts), store the token in an environment variable and pass it with -p "$CCR_TOKEN" rather than hardcoding it in scripts.
Use CCR with Kubernetes (CMK or Self-Managed)
- Kubernetes nodes pull images at pod scheduling time. To authenticate, you must store your CCR credentials as a Kubernetes Secret and reference it in your pod or deployment spec
- Create the secret: replace the placeholders with your actual repository URL, email, and token
kubectl create secret docker-registry ccr-credentials \
--docker-server=registry.us-east1-a.ccr.crusoecloudcompute.com/my-app-repo.7hf6et43 \
--docker-username=your@email.com \
--docker-password='your-ccr-token-here' \
--namespace=my-namespace- Reference the secret in your Deployment manifest using imagePullSecrets:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-training-job
spec:
template:
spec:
containers:
- name: trainer
image: registry.us-east1-a.ccr.crusoecloudcompute.com/my-app-repo.7hf6et43/my-trainer:v1.0
imagePullSecrets:
- name: ccr-credentials💡 Token rotation: CCR tokens can expire. For production clusters, consider using the Crusoe CCR Token Rotator to automatically refresh credentials. See the CCR Token Rotation in Kubernetes documentation.
Example
The following shows a complete end-to-end workflow: building a custom training image with your code baked in, pushing it to CCR, and running it in a Kubernetes pod.
1. Build and push the image (from your local machine)
# Build your custom image
docker build -t registry.us-east1-a.ccr.crusoecloudcompute.com/ml-images.7hf6et43/trainer:v2.1 .
# Log in and push
docker login registry.us-east1-a.ccr.crusoecloudcompute.com/ml-images.7hf6et43 \
-u your@email.com -p 'your-ccr-token'
docker push registry.us-east1-a.ccr.crusoecloudcompute.com/ml-images.7hf6et43/trainer:v2.12. Create the Kubernetes pull secret
kubectl create secret docker-registry ccr-credentials \
--docker-server=registry.us-east1-a.ccr.crusoecloudcompute.com/ml-images.7hf6et43 \
--docker-username=your@email.com \
--docker-password='your-ccr-token' \
--namespace=training3. Submit the training job
apiVersion: batch/v1
kind: Job
metadata:
name: train-v2
namespace: training
spec:
template:
spec:
containers:
- name: trainer
image: registry.us-east1-a.ccr.crusoecloudcompute.com/ml-images.7hf6et43/trainer:v2.1
resources:
limits:
nvidia.com/gpu: "8"
restartPolicy: Never
imagePullSecrets:
- name: ccr-credentialsAdvanced: Pull-Through Cache
A pull-through cache lets CCR act as a local proxy for an external registry. The first time a node pulls an image, CCR fetches it from the upstream source and caches it. Every subsequent pull is served from CCR directly, eliminating external registry rate limits and egress costs.
This is especially useful for large clusters where many nodes simultaneously pull the same NVIDIA NGC or Docker Hub image during a scale-up event.
Example: Cache Docker Hub
crusoe registry repositories create \
--name dockerhub-cache \
--location us-east1-a \
--mode pull-through-cache \
--upstream-registry \
url=https://hub.docker.com,provider=docker-hub,username=myuser,password=mytoken
Example: Cache NVIDIA NGC
crusoe registry repositories create \
--name ngc-cache \
--location us-east1-a \
--mode pull-through-cache \
--upstream-registry \
url=https://nvcr.io,provider=docker-registry,username='$oauthtoken',password=<ngc-api-key>Once created, pull images through the cache by replacing the original registry URL with your CCR pull-through cache URL. CCR handles fetching from the upstream automatically.
Troubleshooting
docker login fails with "unauthorized"
Confirm you are using your Crusoe account email as the username and the CCR token (not your Crusoe password) as the password. Wrap the token in single quotes.
docker push fails with "denied" or "not found"
The repository must exist before you can push. Verify it exists with crusoe registry repositories list. Also confirm the image tag includes the full CCR repository URL.
ImagePullBackOff in Kubernetes
The imagePullSecrets field is missing from the pod spec, or the Secret name is incorrect. Run kubectl describe pod <pod-name> to see the exact error, and verify the Secret exists with kubectl get secret ccr-credentials.
Special characters in token cause shell errors
Always wrap the token in single quotes when passing it on the command line: -p 'your-token'. Double quotes cause the shell to interpret $ and other characters as variables.
Repository deletion fails
A repository must be empty before it can be deleted. First delete all images with:
crusoe registry images delete <image-name> --repo-name <repo> --location <location>
Related Articles
- How-To Expose a Private Docker Registry Behind a Bastion Using an SSH Tunnel and Access It Through Crusoe CCR Pull-Through Caching
- How-To Get Started with Flyte v1 on Crusoe Managed Kubernetes
- How-To Diagnose and Fix CMK Node Disk Pressure
- How-To Install AMD GPU Operator on a Crusoe Managed Kubernetes Cluster