Skip to main content

Just use the Sailbox

Create a Sailbox and set it up the way you would any Linux machine: copy files in with sail box cp, run commands with sail box exec, or enable SSH and use scp and rsync. When it looks right, checkpoint it. Every Sailbox you start from that checkpoint boots with the same disk, and the same memory, already in place:
This is the fastest way to get many identical environments, and it needs no image at all. See Forking.

Use a container image you already have

Point Sailbox.create at an image on a public registry. Sail pulls it and layers what a Sailbox needs on top.
Write the reference as you would for docker pull. The image must be Debian- or Ubuntu-based and publicly pullable from docker.io, ghcr.io, public.ecr.aws, or quay.io. Private registries are not supported.

How Sail treats your image

  • ENV, WORKDIR, and USER become the defaults for every command you run in the Sailbox. Commands run as the image’s USER when it sets one and as root otherwise. Pass user="0:0" on a call to run as root anyway.
  • ENTRYPOINT and CMD are not run. A Sailbox manages its own processes; your commands say what to execute.
  • The image keeps its own python3. Sail never installs another Python over it, because a pinned interpreter would shadow the one the image was built around.
  • A few paths are Sail’s. The build replaces /init and some Sail-owned files under /usr/local/bin, and writes configuration under /etc/sailbox and at /etc/profile.d/sailbox-env.sh. Everything else is left alone.
  • The Sailbox runs on the architecture the image was built for. An image published for both amd64 and arm64 runs on amd64; pass architecture to require one.

Build one from a Dockerfile

Already have a Dockerfile? Sail builds it for you.
context_dir is where COPY and ADD read from, with .dockerignore honored. Every FROM and COPY --from must name a public image on one of the registries above, and the result must be Debian- or Ubuntu-based. When a step fails, the error includes that step’s output.
  • Pass a path or the Dockerfile text itself (contents= in Python, { contents } in TypeScript, DockerfileInput::Contents in Rust).
  • The build runs for amd64 unless you pass architecture. build_args fill ARG instructions like --build-arg. Names starting with BUILDKIT_ and Docker’s proxy variables (HTTP_PROXY and friends) are rejected; a RUN step can set a proxy for itself.
  • A Dockerfile.dockerignore next to the Dockerfile replaces the context’s .dockerignore, and ignore patterns you pass win over both. Python snapshots the context when you call from_dockerfile; TypeScript and Rust do it when the image is built. Edits after that point do not reach the build.
  • The context keeps file modes, empty directories, and symlinks. Hard links arrive as separate files. Setuid, setgid, and sticky bits, named pipes, device nodes, and mode 000 entries are rejected; sockets are skipped.
  • Up to 25 different images per Dockerfile across FROM and COPY --from.
  • Multi-stage builds, tmpfs mounts, and bind mounts from the context or another stage work. RUN --mount of type cache, secret, or ssh, a bind mount whose from names another image, mount options that are variable references, and ONBUILD (in your file or a base image) are rejected.
  • A # syntax= line may declare docker/dockerfile:1 or a release from 1.4 through 1.22.0. Anything else is rejected. The line does not change how the file is built.

Build one in code

No Dockerfile? Start from Sail’s Debian base and chain the steps you need. Each step returns a new definition, so one base can serve several variants.
The same steps chain onto a registry image or a Dockerfile image too. Remote paths must be absolute and cannot contain a space, $, ", or \. Variable names must start with a letter or _ and contain only letters, digits, and _.
sail.Image.debian_amd64 and debian_arm64 also install a Python matching your local interpreter, so that @sail.function can run your Python functions inside the Sailbox. Use sail.Image.debian("amd64", install_python=False) to keep the base’s stock python3.

Building and caching

Pass an image definition to Sailbox.create and Sail uploads any local files, builds the image if it has not been built before, and starts the Sailbox from it. image_build_timeout (imageBuildTimeoutSeconds in TypeScript) bounds the build, retries included. In Rust the timeout is the duration passed to build_image_definition. To build ahead of time instead, call build on the definition and pass the result to Sailbox.create. In Rust, build_image_definition is the ahead-of-time build.
  • Builds are cached by content, per organization. The same base, steps, variables, and file contents reuse the existing image.
  • Tags are pinned, per organization. The first build from a tag such as python:3.13, or from a FROM line, pins the version the tag pointed at, and later builds keep it even after the tag moves upstream. Pass force_build (forceBuild in TypeScript, BuildMode::ForceBuild in Rust) to look the tag up again. That moves the pin for the whole organization; Sailboxes that already exist keep the version they started on. A digest (name@sha256:...) never moves.
  • The first build of a large image downloads all of it. Later builds usually reuse its layers.
After a build, and periodically while an image is in use, Sail boots it outside any Sailbox to capture a start snapshot. Sailboxes created from the image resume from that snapshot instead of cold-booting, which keeps starts fast at every size.This makes two things part of the image contract:
  • Boot-time initialization runs during every hidden boot. systemd units and init scripts must be safe to run repeatedly, outside any Sailbox. Your entrypoint and the commands you run in a Sailbox never run during a hidden boot.
  • State written at boot is shared. Whatever boot leaves on disk or in memory is in the snapshot every Sailbox resumes from. Generate per-instance identity (machine IDs, nonces, cached credentials) at runtime, for example in your application’s entrypoint, not at boot. Environment variables, networking, and Sail-managed credentials are applied per Sailbox after the resume, so they behave the same either way.