Just use the Sailbox
Create a Sailbox and set it up the way you would any Linux machine: copy files in withsail 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:
Use a container image you already have
PointSailbox.create at an image on a public registry. Sail pulls it and
layers what a Sailbox needs on top.
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, andUSERbecome the defaults for every command you run in the Sailbox. Commands run as the image’sUSERwhen it sets one and as root otherwise. Passuser="0:0"on a call to run as root anyway.ENTRYPOINTandCMDare 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
/initand some Sail-owned files under/usr/local/bin, and writes configuration under/etc/sailboxand 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
architectureto 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.
Dockerfile support details
Dockerfile support details
- Pass a path or the Dockerfile text itself (
contents=in Python,{ contents }in TypeScript,DockerfileInput::Contentsin Rust). - The build runs for amd64 unless you pass
architecture.build_argsfillARGinstructions like--build-arg. Names starting withBUILDKIT_and Docker’s proxy variables (HTTP_PROXYand friends) are rejected; aRUNstep can set a proxy for itself. - A
Dockerfile.dockerignorenext to the Dockerfile replaces the context’s.dockerignore, andignorepatterns you pass win over both. Python snapshots the context when you callfrom_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
000entries are rejected; sockets are skipped. - Up to 25 different images per Dockerfile across
FROMandCOPY --from. - Multi-stage builds,
tmpfsmounts, andbindmounts from the context or another stage work.RUN --mountof typecache,secret, orssh, abindmount whosefromnames another image, mount options that are variable references, andONBUILD(in your file or a base image) are rejected. - A
# syntax=line may declaredocker/dockerfile:1or 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 toSailbox.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 aFROMline, pins the version the tag pointed at, and later builds keep it even after the tag moves upstream. Passforce_build(forceBuildin TypeScript,BuildMode::ForceBuildin 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.
Your image may boot when you are not looking
Your image may boot when you are not looking
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.