For the complete documentation index, see llms.txt. This page is also available as Markdown.

Custom container images

Create custom container images for your sandboxes.

Background

Before creating your custom Docker image, ensure that the image you need is not already available on the Docker Hub. Read the Docker documentation to build your own Docker image.

Create images

You have two options for host container images:

  • Host publicly on Docker Hub

  • Host privately in a Google Cloud Artifact Registry (see steps below)

Docker Hub

Host publicly on Docker Hub. When using public Docker hub there is not any required integration or security changes required to run containers within your Instruqt sandbox.

  1. Within your track sandbox configuration select add host

  2. Provide your hostname and container name. For example public docker hub container "busybox" would just require "busybox" to be put in for the container image name. If you have your own namespace you would prefix the name of your repository with that namespace (ie. namespace/repository)

  3. Click save to add the host to the sandbox

Google Cloud Artifact Registry

Host privately in a Google Cloud Artifact Registry (see steps below)

If you opt for private images, you need to authorize Instruqt's service accounts access to your repository. You can do so by granting the role roles/storage.objectViewer on the backing storage and roles/artifactregistry.reader role to the following Instruqt service accounts:

  • serviceAccount:instruqt-participants-nodepool@instruqt-prod.iam.gserviceaccount.com

  • serviceAccount:instruqt-backend@instruqt-prod.iam.gserviceaccount.com

Image streaming (caching) compatibility

Instruqt pulls sandbox container images onto GKE nodes that have image streaming enabled. With streaming, a container starts before its image is fully downloaded, which is the difference between a host that is ready in seconds and one that stalls on a multi-gigabyte pull.

Streaming is best-effort. When an image is not eligible, GKE falls back to a full pull without reporting an error: the container still starts, only much slower. Empty layers and duplicate layers are the most common reasons an image becomes ineligible.

Requirements

  • Store the image in a standard or remote Artifact Registry repository, or in a public registry on Docker Hub. Images hosted anywhere else, such as Quay, GitHub Container Registry, or Amazon ECR, are not eligible.

  • Use an OCI or Docker V2 schema 2 manifest. Images built with the V2 manifest schema 1 format are not eligible; rebuilding with any current version of Docker or BuildKit produces a supported manifest.

  • Build for linux/amd64.

  • Keep the image free of empty and duplicate layers.

  • Size must be greater than 10 MB.

Avoid empty layers

An empty layer is a layer whose filesystem diff contains no changes. Empty layers always carry the same well-known digest, sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4, so two of them in one image are duplicates as well: a single stray RUN repeated twice creates both problems at once.

Common sources of empty layers:

  • RUN steps that change nothing on disk, such as RUN true, RUN echo "building app", or RUN ls -la /opt.

  • COPY or ADD of a directory that contains no files.

  • VOLUME declarations, which create a mount point but no content.

Collapse related commands into a single layer instead:

Metadata instructions such as ENV, LABEL, WORKDIR, USER, EXPOSE, CMD, and ENTRYPOINT do not create filesystem layers when you build with BuildKit, so they are safe to use freely.

Avoid duplicate layers

A duplicate layer is the same layer digest appearing more than once in an image manifest. Common sources:

  • Copying identical content into the final image twice, for example two COPY --from=builder steps that resolve to the same files.

  • Multi-stage builds where two stages run the same install commands and both are copied into the final image.

  • Rebasing or appending layers onto a base image that already contains those exact layers.

  • More than one empty layer.

Give each layer distinct content, and copy any given artifact into the final stage exactly once.

Verify an image before you use it

Inspect a local image with docker. Duplicate layers print a digest; a clean image prints nothing:

Empty layers show up as 0B steps in the build history:

For an image already pushed to a registry, inspect the manifest with crane:

Fix an existing image

If you cannot rebuild from the Dockerfile, flatten the image into a single layer. This removes every empty and duplicate layer:

Flattening trades layer reuse for compatibility. A flattened image shares no layers with other images in the repository, so it costs more storage and takes longer to pull on the fallback path. Fixing the Dockerfile is the better long-term option.

Last updated

Was this helpful?