Docker
anycloud deploys Docker containers to cloud VMs. If you're new to Docker, here's what you need to know.
What is Docker?
Docker packages your application and its dependencies into a container — a lightweight, portable unit that runs the same way everywhere. Think of it as a zip file for your entire app environment.
Images and Containers
- Image — a blueprint for your app (code + dependencies + OS). Built from a
Dockerfile. - Container — a running instance of an image. anycloud creates one container per VM.
Using Existing Images
You can run any public image from Docker Hub, GHCR, or other registries without building anything:
anycloud submit python:3.11 -- python train.py
anycloud submit pytorch/pytorch:2.1.0-cuda12.1-cudnn8-runtime --gpu-type h100:8 --gpus all
This is the fastest way to get started — pick an image that has the runtime you need and pass your command directly.
Building and Pushing
You often don't need to. If a public image already has the runtime you need (PyTorch, CUDA, etc.), run it directly — pass your command to anycloud submit, or use the @anycloud.function() decorator, which clones your code onto the VM from git at run time instead of baking it into an image:
# Stock PyTorch image + your code from git — no custom image, no build step
@anycloud.function(image="pytorch/pytorch:2.2.0-cuda12.1-cudnn8-runtime", gpu="h100:8")
def train(lr: float):
...
(The image just needs git installed — most ML images have it.)
Build a custom image only when you need dependencies no off-the-shelf image provides. A Dockerfile describes it:
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "train.py"]
anycloud runs a prebuilt image — it does not build one for you. Build and push your image to a registry first, then anycloud submit it. GitHub Container Registry (GHCR) works with no extra setup, because anycloud already authenticates you with GitHub.
Build locally
anycloud login logs your local Docker CLI into GHCR for you (whenever Docker is installed), so there's no separate registry credential to set up — just build and push:
docker buildx build \
--platform linux/amd64 \
-t ghcr.io/YOUR_GITHUB_USERNAME/my-training:latest \
--push .
Use buildx --platform linux/amd64 when building locally, especially on Apple Silicon Macs. The cloud GPU VMs anycloud provisions run Linux on x86_64; a plain docker build on an M-series Mac may publish an arm64 image that pulls successfully but cannot run on the target VM.
For GPU workloads, start from a CUDA/NVIDIA or framework GPU image such as nvidia/cuda:*, pytorch/pytorch:*cuda*, or an image you have tested on Linux with NVIDIA drivers. Building on a Mac will not validate GPU access. Avoid Dockerfile build steps that require a local GPU; if you compile CUDA extensions during image build, prefer Linux/amd64 CI or a Linux NVIDIA builder.
Then submit it:
anycloud submit ghcr.io/YOUR_GITHUB_USERNAME/my-training:latest --gpu-type h100:8 --gpus all -- python train.py
Push rejected with
denied/401? Re-runanycloud loginto re-authenticate Docker — the stored credential is only as fresh as your GitHub token, which Docker never refreshes on its own.
Build in CI (GitHub Actions)
Recommended for repeatable builds: each image is tied to the commit that built it, and there's no token to manage — GitHub Actions provides a GITHUB_TOKEN with package-write permission. A minimal workflow that builds and pushes on every commit:
name: build-image
on: push
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v6
- name: Lowercase image name
run: echo "IMAGE=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV"
- uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64
push: true
tags: ${{ env.IMAGE }}:latest
(GHCR image names must be lowercase; github.repository keeps the owner/repo casing, so the lowercase step keeps the example working for any repo.)
This ties each image to the commit that built it, which pairs naturally with how @anycloud.function() syncs code by git commit. Any other CI works the same way: build, docker login ghcr.io, push.
Whichever path you use, anycloud only needs a pullable image reference at submit time.
Submitting
anycloud submit runs any image — public or private — on a cloud VM:
anycloud submit ghcr.io/user/train:latest --gpu-type h100:8 --gpus all
For private images, anycloud supports GHCR — anycloud login authenticates via GitHub OAuth, and anycloud uses your token to pull the image on the VM. Other private registries are not currently supported.
If you use the @anycloud.function() decorator, your code is synced via git instead of being baked into the image — you only need to rebuild when dependencies change. See Deployment Workflows for when to use each approach.
How anycloud Uses Docker
When you run anycloud submit:
- anycloud provisions a VM in your cloud account
- Installs Docker on the VM
- Pulls your image from the registry (for private GHCR images, authenticates with your GitHub token)
- Starts your container with
docker run
Your container runs as if it were on your local machine — same environment, same dependencies, same behavior. AnyCloud resolves the image digest and pulls the container image onto each new workload VM.
eStargz acceleration
Large images — the GPU and machine-learning images where a multi-gigabyte download dominates startup — can spend most of their launch time just pulling. If your image is published in eStargz format, anycloud can start it lazily where the runtime supports it: the container runs while its layers are fetched on demand, so it doesn't have to finish downloading first. It's an optimization — small or ordinary images, and hosts without lazy-loading support, pull the normal way.
anycloud status shows which path a deployment took on its running event:
[lazy-loaded] when the image loaded lazily, or [full pull] when it was
downloaded up front.
Producing an eStargz image
An eStargz image is an ordinary OCI image — it still pulls and runs anywhere, on any registry — whose layers carry a seekable table of contents so a snapshotter can fetch files on demand. There's no separate index artifact and no special registry support to arrange.
If you build the image yourself, fold it into your existing build — one flag, no
extra step (needs BuildKit / docker buildx):
docker buildx build -t ghcr.io/YOUR_ORG/model:latest \
-o type=registry,oci-mediatypes=true,compression=estargz,force-compression=true \
.
To convert an image you've already built, without rebuilding it, use the upstream
nerdctl CLI (CPU-only, runs anywhere):
nerdctl image convert --estargz --oci \
ghcr.io/YOUR_ORG/model:latest ghcr.io/YOUR_ORG/model:esgz
nerdctl push ghcr.io/YOUR_ORG/model:esgz
Either way, submit the image the normal way:
anycloud submit ghcr.io/YOUR_ORG/model:latest --gpu-type h100:8 --gpus all -- python train.py
There's no --estargz flag — anycloud detects the format at submit time,
lazy-loads it where the runtime supports it, and pulls it normally everywhere
else. Check which path a run took with anycloud status <id>: the running event
is marked [lazy-loaded] or [full pull]. Today this covers linux/amd64
images on GHCR.
Docker Runtime Options
You can pass standard Docker runtime flags to control resource limits for any workload:
anycloud submit my-image --shm-size 8g --memory 32g --cpus 4 --ipc host
| Flag | Description |
|---|---|
--shm-size | Shared memory size (e.g., 8g) |
--memory | Memory limit (e.g., 32g) |
--cpus | CPU limit |
--ipc | IPC mode (host, private, shareable) |
--ulimit | Ulimit settings (can specify multiple) |
GPU Support
When you request a GPU (a --gpu-type, or a GPU --vm-type), anycloud sets up GPU access for you — your container sees the GPU with no extra flags, and you don't need --runtime nvidia.
Pass --gpus to set a specific device count (e.g. --gpus 2):
anycloud submit my-image --gpus 2
| Flag | Description |
|---|---|
--gpus | GPU access (all or count) |