Container Registry
Carolina Cloud runs a private container registry at registry.carolinacloud.io. It’s a normal
Docker registry — docker login, docker push, docker pull all work exactly like they do
against Docker Hub, ECR, or GHCR. Use it to bring your own tool containers onto the platform
without publishing them anywhere public.
This is most useful for Nextflow: any process with a container directive can point at an image
you’ve pushed here, the same way you’d point it at a public image on Docker Hub.
1. Log in
Section titled “1. Log in”Your Carolina Cloud API key is the password. The username can be anything — Docker requires a non-empty value, but it isn’t checked.
docker login registry.carolinacloud.io -u ccloud --password-stdin <<< "$CCLOUD_API_KEY"Get your API key from API Key in the console if you don’t have it set as an environment variable.
2. Push an image
Section titled “2. Push an image”Images are namespaced by your organization, so every image you push lives under
registry.carolinacloud.io/<your-org-id>/<name>:<tag>. Your account team can confirm your org id
if you don’t already have it.
docker build -t registry.carolinacloud.io/<org-id>/my-tool:1.0 .docker push registry.carolinacloud.io/<org-id>/my-tool:1.0Already have an image built and tagged elsewhere? Just retag it — no rebuild needed:
docker tag my-tool:1.0 registry.carolinacloud.io/<org-id>/my-tool:1.0docker push registry.carolinacloud.io/<org-id>/my-tool:1.03. Use it
Section titled “3. Use it”Pull it back down to confirm it landed:
docker pull registry.carolinacloud.io/<org-id>/my-tool:1.0Or reference it directly in a Nextflow process — Carolina Cloud pool workers pull it automatically when the task runs:
process MY_TOOL { container 'registry.carolinacloud.io/<org-id>/my-tool:1.0'
script: """ my-tool --input ${input} --output result.txt """}Images stay private to your organization — nobody outside it can pull or push to your namespace.
Migrating from another registry
Section titled “Migrating from another registry”If your images already live in ECR, GHCR, or Docker Hub, there’s no export/import step — pull, retag, push:
docker pull <your-existing-registry>/<repo>:<tag>docker tag <your-existing-registry>/<repo>:<tag> registry.carolinacloud.io/<org-id>/<repo>:<tag>docker push registry.carolinacloud.io/<org-id>/<repo>:<tag>For ECR specifically, authenticate to it first with:
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.comA note on architecture
Section titled “A note on architecture”Carolina Cloud’s compute fleet runs linux/amd64. If you build or pull images on an Apple
Silicon Mac, you’ll get its native arm64 build by default — which fails to start with an
exec format error when a task tries to run it. Build or pull with an explicit platform to avoid
this:
docker pull --platform linux/amd64 <image>docker build --platform linux/amd64 -t <image> .Moving from ECR? Copy this LLM prompt
Section titled “Moving from ECR? Copy this LLM prompt”If you’d rather have an AI agent do the migration for you, paste this into Claude Code (or any coding agent) after filling in the placeholders. It walks through spinning up a Carolina Cloud VM (avoids the Apple Silicon architecture issue above), configuring AWS access, and moving your images over.
You're helping me migrate my Docker container images from AWS ECR to Carolina Cloud's privatecontainer registry, so my Nextflow pipelines can pull them directly instead of using AWS.
Do this on a fresh Carolina Cloud VM (not my laptop) — Carolina Cloud's fleet is x86_64, and if Ibuild/pull images on my own machine (especially if it's Apple Silicon) I'll get an`exec format error` when the pipeline tries to run them. Doing everything on a Carolina Cloud VMsidesteps that entirely.
My Carolina Cloud org id is: <ORG_ID>My Carolina Cloud API key is: <API_KEY>My AWS credentials (for ECR read access) are: <AWS_ACCESS_KEY_ID> / <AWS_SECRET_ACCESS_KEY> (region: <AWS_REGION>)
## Step 1: Create a Carolina Cloud VM
Use the API/CLI docs at https://docs.carolinacloud.io/api-cli/api/ and the interactive schema athttps://api.carolinacloud.io/api/docs/ to create a VM — use resource_type "vm", not "container"(a container runs on our shared Docker daemon with no nested Docker of its own; a VM gives you areal, independent one). 50GB+ disk is plenty for temporary image layers. Use my API key above asthe Bearer token for the creation request. Once it's up, SSH in and confirm `docker` works(`docker version`); install it if the flavor doesn't ship it (`apt-get install -y docker.io` onDebian/Ubuntu-based images).
Export my Carolina Cloud API key as an env var on that VM so later steps can reference it withoutme pasting it again:```export CCLOUD_API_KEY=<API_KEY>```
## Step 2: Configure AWS access for ECR
Install the AWS CLI if it's not already there, then configure it with the credentials above:```aws configure set aws_access_key_id <AWS_ACCESS_KEY_ID>aws configure set aws_secret_access_key <AWS_SECRET_ACCESS_KEY>aws configure set region <AWS_REGION>```Confirm it works: `aws sts get-caller-identity`. The IAM user/role needs at minimum`ecr:GetAuthorizationToken`, `ecr:BatchGetImage`, `ecr:GetDownloadUrlForLayer`, and`ecr:DescribeRepositories`/`ecr:ListImages` (to enumerate what's there).
## Step 3: Log in to both registries
ECR:```aws ecr get-login-password --region <AWS_REGION> | docker login --username AWS --password-stdin <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com```
Carolina Cloud's registry — the username is arbitrary (Docker requires a non-empty one), thepassword is my Carolina Cloud API key from above:```docker login registry.carolinacloud.io -u pipeline --password-stdin <<< "$CCLOUD_API_KEY"```
## Step 4: Bring the images over
For each image I want to migrate, pull from ECR, retag under my org's namespace on CarolinaCloud's registry, and push:```docker pull <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/<repo>:<tag>docker tag <AWS_ACCOUNT_ID>.dkr.ecr.<AWS_REGION>.amazonaws.com/<repo>:<tag> registry.carolinacloud.io/<ORG_ID>/<repo>:<tag>docker push registry.carolinacloud.io/<ORG_ID>/<repo>:<tag>```
If I give you a list of ECR repos/tags, script this as a loop rather than doing it one at a time.List what's in ECR first with `aws ecr describe-repositories` and `aws ecr list-images --repository-name <repo>`if I haven't already told you exactly which ones I need.
Once pushed, I'll reference these in my Nextflow `container` directives as`registry.carolinacloud.io/<ORG_ID>/<repo>:<tag>` — verify each one pulls back down cleanly(`docker pull registry.carolinacloud.io/<ORG_ID>/<repo>:<tag>` after removing it locally) beforetelling me you're done.Image requirements
Section titled “Image requirements”Nextflow’s task wrapper needs a couple of things present in any image used in a container
directive, regardless of what your own script does:
- A shell (
/bin/bashor/bin/sh) — extremely minimal from-scratch images may not have one. ps(from theprocpspackage) — used for task resource metrics. Most base images (Ubuntu, Debian, Alpine) either include it or need one extra package installed (apt-get install -y procps/apk add procps).
Real-world bioinformatics images (biocontainers, nf-core module containers, etc.) almost always have both already.