Skip to content

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.

Your Carolina Cloud API key is the password. The username can be anything — Docker requires a non-empty value, but it isn’t checked.

Terminal window
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.

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.

Terminal window
docker build -t registry.carolinacloud.io/<org-id>/my-tool:1.0 .
docker push registry.carolinacloud.io/<org-id>/my-tool:1.0

Already have an image built and tagged elsewhere? Just retag it — no rebuild needed:

Terminal window
docker tag my-tool:1.0 registry.carolinacloud.io/<org-id>/my-tool:1.0
docker push registry.carolinacloud.io/<org-id>/my-tool:1.0

Pull it back down to confirm it landed:

Terminal window
docker pull registry.carolinacloud.io/<org-id>/my-tool:1.0

Or 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.

If your images already live in ECR, GHCR, or Docker Hub, there’s no export/import step — pull, retag, push:

Terminal window
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:

Terminal window
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com

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:

Terminal window
docker pull --platform linux/amd64 <image>
docker build --platform linux/amd64 -t <image> .

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 private
container 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 I
build/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 VM
sidesteps 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 at
https://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 a
real, independent one). 50GB+ disk is plenty for temporary image layers. Use my API key above as
the 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` on
Debian/Ubuntu-based images).
Export my Carolina Cloud API key as an env var on that VM so later steps can reference it without
me 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), the
password 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 Carolina
Cloud'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) before
telling me you're done.

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/bash or /bin/sh) — extremely minimal from-scratch images may not have one.
  • ps (from the procps package) — 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.