Sign in

Run Docker and Compose in a sandbox

Updated

Run Docker, Compose and docker build in a Prized sandbox on a workspace that has paid in: create from prized-docker-v1, start your stack once, pause it, and fork it into live copies with the containers still running, one per job or RL episode.

On this page8

prized-docker-v1 is a sandbox template with Docker Engine, Compose and Buildx on top of everything in prized-base-v1 (Templates). docker works as the user prized, without sudo. It is for workspaces that have paid in.

1. Sign up and add funds

Open Sandboxes to sign up, then start a plan or buy a top-up at https://prized.dev/dashboard/billing. On the free credit alone, a create, fork, or resume from prized-docker-v1 is refused: the API answers 402 template_needs_payment with a billingUrl, and the CLI prints the refusal with its link to Billing.

2. Install the CLI and sign in

curl -fsSL https://prized.dev/install.sh | sh

Open a new terminal so prized is on your PATH, and skip the prized setup the installer suggests: it sets up a box. Then sign in, or export PRIZED_API_KEY instead, a key with Sandboxes: Write from the API keys page (CLI: Sandboxes):

prized login

3. Create a Docker sandbox

ID=$(prized sandbox create --template prized-docker-v1 --disk 30 --max-runtime 2h)
prized sandbox exec $ID -- docker run --rm hello-world
# Hello from Docker!

Images fill the disk, which starts at 10 GB and keeps the size it was created with, so ask for room at the create: --disk 30 leaves room for most stacks, --disk takes up to 128 GB, and docker system prune frees the space images and build cache take. --max-runtime 2h deletes the sandbox after two hours of running, so one left running stops billing; leave it out for the default 24 hours (Run time). A Docker sandbox boots fresh at every create, which takes about 6 seconds to reach running, before any pull or build; a fork's copies skip the pulls and the build, so to start many, set one up and fork it (step 5).

4. Start a Compose stack

Save this as compose.yaml in the folder you run the commands from:

compose.yaml
services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: example
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 2s
      retries: 30

Copy it in and bring it up; --wait returns once every service is healthy:

prized sandbox cp compose.yaml $ID:compose.yaml
prized sandbox exec $ID --timeout 10m -- 'docker compose up -d --wait'
prized sandbox exec $ID -- 'docker compose exec -T db psql -U postgres -tAc "select 1"'
# 1

Your own images build the same way. prized sandbox cp copies one file of up to 100 MB, so send the build context as an archive from your project's folder, and give the build more than a command's default 5 minutes:

tar czf /tmp/context.tgz .
prized sandbox cp /tmp/context.tgz $ID:context.tgz
prized sandbox exec $ID --timeout 30m -- 'mkdir -p app && tar xzf context.tgz -C app && docker build -t app app'

5. Set it up once, fork it for each run

For an RL environment, an eval, or tests that need services: pull and build once, start the stack, pause, and fork. Each copy starts with the containers already running, as the parent had them, with its images and build cache, so it skips the pulls and the build:

prized sandbox pause $ID --wait
COPIES=($(prized sandbox fork $ID --count 4))
for C in "${COPIES[@]}"; do prized sandbox exec $C -- 'docker compose exec -T db psql -U postgres -tAc "select 1"'; done
# 1, from each copy

Run one job in each copy, then delete the copies; the parent stays paused, billing storage only, ready for the next batch:

prized sandbox rm "${COPIES[@]}"
  • How many fit. Copies start on the parent's machine, as many as fit there; for a wider run, set up more than one parent (Fork). Each copy counts its whole disk toward the workspace's 500 GB, paused or not: at --disk 30, that is 16 sandboxes (Limits).
  • Run time. Copies get the parent's two hours; for longer jobs, pass the fork its own --max-runtime.
  • Keep the parent paused. A command or a file copy sent to a paused sandbox resumes it, and a running parent bills compute and is deleted when its run time ends. After you change the parent, pause it again.
  • A paused parent lives on one machine and is lost if that machine fails, so keep the files that rebuild it (Pause and resume).
  • When you are done, delete the parent too: prized sandbox rm $ID.
  • Each copy is its own machine, with its own hostname and IP address. Restart anything in a container that makes keys, tokens or IDs (Do copies share randomness or identity?).

Docker Hub pulls

Docker Hub limits pulls made without signing in, counted per public IP address (Docker Hub pull usage and limits). Sandboxes on the same machine share one outbound address, so a pull can stop at toomanyrequests even when your own sandboxes have pulled little.

  • Pull once, then fork. For many copies of one environment, pull the image in one sandbox and fork it: each copy starts with the image already there and pulls nothing.
  • Sign in, before you pause the parent. Signing in is Docker's documented way to a higher limit. With a Docker Hub access token in DOCKERHUB_TOKEN on your machine and your Docker Hub username for YOUR_USER, run prized sandbox exec $ID --env DOCKERHUB_TOKEN="$DOCKERHUB_TOKEN" -- 'echo "$DOCKERHUB_TOKEN" | docker login -u YOUR_USER --password-stdin'. docker login saves the sign-in in the sandbox's files, which a fork copies, so code in the sandbox and in its copies can read it: use a read-only token.
  • Name the image at a mirror that carries it, for example mirror.gcr.io/library/postgres:16-alpine in place of postgres:16-alpine. A mirror carries popular images, not every image. Your own registry works the same way.

What to know

  • Docker is root inside the sandbox. Docker's daemon runs as root, so code that can use Docker is root inside that sandbox's microVM. Commands themselves run as prized, without sudo.
  • Bind-mount paths under /home/prized, not /tmp: Docker's daemon sees a different /tmp from your commands.
  • Pause keeps containers. A paused sandbox keeps its running containers, and they carry on when it resumes (Pause and resume).
  • Ports. A port published with -p answers inside the sandbox, and, over HTTP and WebSockets, at the sandbox's port URL like any port it listens on.
  • Docker in Docker. A privileged docker:dind container runs its own Docker daemon, for tools that start one.
  • Not yet: GPUs, and egress allow or deny lists (Not yet).
  • Cost. It bills like any sandbox, by vCPU, memory, and disk size (Billing: Sandboxes).

Over the API

Create with {"template": "prized-docker-v1", "resources": {"diskGb": 30}} on POST /api/v1/sandboxes (Sandbox API: Create), then send each docker command to /process/execute on the sandbox's toolboxBase like any other command, with a timeout long enough for pulls and builds (Run a command).