Declarative Builder
Declarative Builder provides a powerful, code-first approach to defining dependencies for Daytona Sandboxes. Instead of importing images from a container registry, you can programmatically define them using the Daytona SDK.
The declarative builder system supports two primary workflows:
- Declarative images: build images on demand when creating sandboxes
- Pre-built snapshots: create and register ready-to-use snapshots
Build declarative images
Section titled “Build declarative images”Daytona provides an option to create declarative images on-the-fly when creating sandboxes. This is ideal for iterating quickly without creating separate snapshots.
Declarative images are cached for 24 hours, and are automatically reused when running the same script. Thus, subsequent runs on the same runner will be almost instantaneous.
# Define a declarative image with python packagesdeclarative_image = ( Image.debian_slim("3.12") .pip_install(["requests", "pytest"]) .workdir("/home/daytona"))
# Create a new sandbox with the declarative image and stream the build logssandbox = daytona.create( CreateSandboxFromImageParams(image=declarative_image), timeout=0, on_snapshot_create_logs=print,)// Define a declarative image with python packagesconst declarativeImage = Image.debianSlim('3.12') .pipInstall(['requests', 'pytest']) .workdir('/home/daytona')
// Create a new sandbox with the declarative image and stream the build logsconst sandbox = await daytona.create( { image: declarativeImage, }, { timeout: 0, onSnapshotCreateLogs: console.log, })# Define a simple declarative image with Python packagesdeclarative_image = Daytona::Image .debian_slim('3.12') .pip_install(['requests', 'pytest']) .workdir('/home/daytona')
# Create a new Sandbox with the declarative image and stream the build logssandbox = daytona.create( Daytona::CreateSandboxFromImageParams.new(image: declarative_image), on_snapshot_create_logs: proc { |chunk| puts chunk })// Define a declarative image with python packagesversion := "3.12"declarativeImage := daytona.DebianSlim(&version). PipInstall([]string{"requests", "pytest"}). Workdir("/home/daytona")
// Create a new sandbox with the declarative image and stream the build logslogChan := make(chan string)go func() { for log := range logChan { fmt.Print(log) }}()
sandbox, err := client.Create(ctx, types.ImageParams{ Image: declarativeImage,}, options.WithTimeout(0), options.WithLogChannel(logChan))if err != nil { // handle error}// Define a declarative image with python packagesImage declarativeImage = Image.debianSlim("3.12") .pipInstall("requests", "pytest") .workdir("/home/daytona");
// Create a new sandbox with the declarative image and stream the build logsCreateSandboxFromImageParams params = new CreateSandboxFromImageParams();params.setImage(declarativeImage);Sandbox sandbox = daytona.create(params, 0L, System.out::println);Create pre-built Snapshots
Section titled “Create pre-built Snapshots”Daytona provides an option to create pre-built snapshots that can be reused across multiple sandboxes.
The snapshot remains visible in the Daytona Dashboard ↗ and is permanently cached, ensuring instant availability without rebuilding.
# Create a python data science imagesnapshot_name = "data-science-snapshot"
image = ( Image.debian_slim("3.12") .pip_install(["pandas", "numpy"]) .workdir("/home/daytona"))
# Create the snapshot and stream the build logsdaytona.snapshot.create( CreateSnapshotParams( name=snapshot_name, image=image, ),on_logs=print,)
# Create a new sandbox using the pre-built snapshotsandbox = daytona.create(CreateSandboxFromSnapshotParams(snapshot=snapshot_name))// Create a python data science imageconst snapshotName = 'data-science-snapshot'
const image = Image.debianSlim('3.12') .pipInstall(['pandas', 'numpy']) .workdir('/home/daytona')
// Create the snapshot and stream the build logsawait daytona.snapshot.create( { name: snapshotName, image, }, { onLogs: console.log, })
// Create a new sandbox using the pre-built snapshotconst sandbox = await daytona.create({ snapshot: snapshotName,})# Create a simple Python data science imagesnapshot_name = 'data-science-snapshot'
image = Daytona::Image .debian_slim('3.12') .pip_install(['pandas', 'numpy']) .workdir('/home/daytona')
# Create the Snapshot and stream the build logsdaytona.snapshot.create( Daytona::CreateSnapshotParams.new( name: snapshot_name, image: image ), on_logs: proc { |chunk| puts chunk })
# Create a new Sandbox using the pre-built Snapshotsandbox = daytona.create( Daytona::CreateSandboxFromSnapshotParams.new(snapshot: snapshot_name))// Create a python data science imagesnapshotName := "data-science-snapshot"
version := "3.12"image := daytona.DebianSlim(&version). PipInstall([]string{"pandas", "numpy"}). Workdir("/home/daytona")
// Create the snapshot and stream the build logs_, logChan, err := client.Snapshot.Create(ctx, &types.CreateSnapshotParams{ Name: snapshotName, Image: image,})if err != nil { // handle error}for log := range logChan { fmt.Print(log)}
// Create a new sandbox using the pre-built snapshotsandbox, err := client.Create(ctx, types.SnapshotParams{ Snapshot: snapshotName,})if err != nil { // handle error}// Create a python data science imageString snapshotName = "data-science-snapshot";
Image image = Image.debianSlim("3.12") .pipInstall("pandas", "numpy") .workdir("/home/daytona");
// Create the snapshot and stream the build logsdaytona.snapshot().create(snapshotName, image, System.out::println);
// Create a new sandbox using the pre-built snapshotCreateSandboxFromSnapshotParams snapshotParams = new CreateSandboxFromSnapshotParams();snapshotParams.setSnapshot(snapshotName);Sandbox sandbox = daytona.create(snapshotParams);Image configuration
Section titled “Image configuration”Daytona provides an option to define images programmatically using the Daytona SDK. You can specify base images, install packages, add files, set environment variables, and more.
For a complete API reference and method signatures, see the Python, TypeScript, Ruby, Go, and Java SDK references.
Base image selection
Section titled “Base image selection”Daytona provides an option to select base images. The following snippets demonstrate how to select and configure base images:
# Create an image from a baseimage = Image.base("python:3.12-slim-bookworm")
# Use a Debian slim image with Python 3.12image = Image.debian_slim("3.12")// Create an image from a baseconst image = Image.base('python:3.12-slim-bookworm')
// Use a Debian slim image with Python 3.12const image = Image.debianSlim('3.12')# Create an image from a baseimage = Daytona::Image.base('python:3.12-slim-bookworm')
# Use a Debian slim image with Python 3.12image = Daytona::Image.debian_slim('3.12')// Create an image from a baseimage := daytona.Base("python:3.12-slim-bookworm")
// Use a Debian slim image with Python 3.12version := "3.12"image := daytona.DebianSlim(&version)// Create an image from a baseImage image = Image.base("python:3.12-slim-bookworm");
// Use a Debian slim image with Python 3.12image = Image.debianSlim("3.12");Package management
Section titled “Package management”Daytona provides an option to install packages and dependencies to your image. The following snippets demonstrate how to install packages and dependencies to your image:
# Add pip packagesimage = Image.debian_slim("3.12").pip_install(["requests", "pandas"])
# Install from requirements.txtimage = Image.debian_slim("3.12").pip_install_from_requirements("requirements.txt")
# Install from pyproject.toml (with optional dependencies)image = Image.debian_slim("3.12").pip_install_from_pyproject("pyproject.toml", optional_dependencies=["dev"])// Add pip packagesconst image = Image.debianSlim('3.12').pipInstall(['requests', 'pandas'])
// Install from requirements.txtconst image = Image.debianSlim('3.12').pipInstallFromRequirements('requirements.txt')
// Install from pyproject.toml (with optional dependencies)const image = Image.debianSlim('3.12').pipInstallFromPyproject('pyproject.toml', { optionalDependencies: ['dev']})# Add pip packagesimage = Daytona::Image.debian_slim('3.12').pip_install(['requests', 'pandas'])
# Install from requirements.txtimage = Daytona::Image.debian_slim('3.12').pip_install_from_requirements('requirements.txt')
# Install from pyproject.toml (with optional dependencies)image = Daytona::Image.debian_slim('3.12').pip_install_from_pyproject('pyproject.toml', optional_dependencies: ['dev'])// Add pip packagesversion := "3.12"image := daytona.DebianSlim(&version).PipInstall([]string{"requests", "pandas"})
// Install from requirements.txtimage := daytona.DebianSlim(&version). AddLocalFile("requirements.txt", "/tmp/requirements.txt"). Run("pip install -r /tmp/requirements.txt")
// Install from pyproject.toml (with optional dependencies)image := daytona.DebianSlim(&version). AddLocalFile("pyproject.toml", "/tmp/pyproject.toml"). Run("pip install /tmp[dev]")// Add pip packagesImage image = Image.debianSlim("3.12").pipInstall("requests", "pandas");File system operations
Section titled “File system operations”Daytona provides an option to add files and directories to your image. The following snippets demonstrate how to add files and directories to your image:
# Add a local fileimage = Image.debian_slim("3.12").add_local_file("package.json", "/home/daytona/package.json")
# Add a local directoryimage = Image.debian_slim("3.12").add_local_dir("src", "/home/daytona/src")// Add a local fileconst image = Image.debianSlim('3.12').addLocalFile('package.json', '/home/daytona/package.json')
// Add a local directoryconst image = Image.debianSlim('3.12').addLocalDir('src', '/home/daytona/src')# Add a local fileimage = Daytona::Image.debian_slim('3.12').add_local_file('package.json', '/home/daytona/package.json')
# Add a local directoryimage = Daytona::Image.debian_slim('3.12').add_local_dir('src', '/home/daytona/src')// Add a local fileversion := "3.12"image := daytona.DebianSlim(&version).AddLocalFile("package.json", "/home/daytona/package.json")
// Add a local directoryimage := daytona.DebianSlim(&version).AddLocalDir("src", "/home/daytona/src")Environment configuration
Section titled “Environment configuration”Daytona provides an option to configure environment variables and working directories. The following snippets demonstrate how to configure environment variables and working directories:
# Set environment variablesimage = Image.debian_slim("3.12").env({"PROJECT_ROOT": "/home/daytona"})
# Set working directoryimage = Image.debian_slim("3.12").workdir("/home/daytona")// Set environment variablesconst image = Image.debianSlim('3.12').env({ PROJECT_ROOT: '/home/daytona' })
// Set working directoryconst image = Image.debianSlim('3.12').workdir('/home/daytona')# Set environment variablesimage = Daytona::Image.debian_slim('3.12').env({ 'PROJECT_ROOT' => '/home/daytona' })
# Set working directoryimage = Daytona::Image.debian_slim('3.12').workdir('/home/daytona')// Set environment variablesversion := "3.12"image := daytona.DebianSlim(&version).Env("PROJECT_ROOT", "/home/daytona")
// Set working directoryimage := daytona.DebianSlim(&version).Workdir("/home/daytona")// Set environment variablesImage image = Image.debianSlim("3.12") .env(java.util.Map.of("PROJECT_ROOT", "/home/daytona"));
// Set working directoryimage = Image.debianSlim("3.12").workdir("/home/daytona");Commands and entrypoints
Section titled “Commands and entrypoints”Daytona provides an option to execute commands during build and configure container startup behavior. The following snippets demonstrate how to execute commands during build and configure container startup behavior:
# Run shell commands during buildimage = Image.debian_slim("3.12").run_commands( 'apt-get update && apt-get install -y git', 'groupadd -r daytona && useradd -r -g daytona -m daytona', 'mkdir -p /home/daytona/workspace')
# Set entrypointimage = Image.debian_slim("3.12").entrypoint(["/bin/bash"])
# Set default commandimage = Image.debian_slim("3.12").cmd(["/bin/bash"])// Run shell commands during buildconst image = Image.debianSlim('3.12').runCommands( 'apt-get update && apt-get install -y git', 'groupadd -r daytona && useradd -r -g daytona -m daytona', 'mkdir -p /home/daytona/workspace')
// Set entrypointconst image = Image.debianSlim('3.12').entrypoint(['/bin/bash'])
// Set default commandconst image = Image.debianSlim('3.12').cmd(['/bin/bash'])# Run shell commands during buildimage = Daytona::Image.debian_slim('3.12').run_commands( 'apt-get update && apt-get install -y git', 'groupadd -r daytona && useradd -r -g daytona -m daytona', 'mkdir -p /home/daytona/workspace')
# Set entrypointimage = Daytona::Image.debian_slim('3.12').entrypoint(['/bin/bash'])
# Set default commandimage = Daytona::Image.debian_slim('3.12').cmd(['/bin/bash'])// Run shell commands during buildversion := "3.12"image := daytona.DebianSlim(&version). Run("apt-get update && apt-get install -y git"). Run("groupadd -r daytona && useradd -r -g daytona -m daytona"). Run("mkdir -p /home/daytona/workspace")
// Set entrypointimage := daytona.DebianSlim(&version).Entrypoint([]string{"/bin/bash"})
// Set default commandimage := daytona.DebianSlim(&version).Cmd([]string{"/bin/bash"})// Run shell commands during buildImage image = Image.debianSlim("3.12").runCommands( "apt-get update && apt-get install -y git", "groupadd -r daytona && useradd -r -g daytona -m daytona", "mkdir -p /home/daytona/workspace");
// Set entrypointimage = Image.debianSlim("3.12").entrypoint("/bin/bash");
// Set default commandimage = Image.debianSlim("3.12").cmd("/bin/bash");Dockerfile integration
Section titled “Dockerfile integration”Daytona provides an option to integrate existing Dockerfiles or add custom Dockerfile commands. The following snippets demonstrate how to integrate existing Dockerfiles or add custom Dockerfile commands:
# Add custom Dockerfile commandsimage = Image.debian_slim("3.12").dockerfile_commands(["RUN echo 'Hello, world!'"])
# Use an existing Dockerfileimage = Image.from_dockerfile("Dockerfile")
# Extend an existing Dockerfileimage = Image.from_dockerfile("app/Dockerfile").pip_install(["numpy"])// Add custom Dockerfile commandsconst image = Image.debianSlim('3.12').dockerfileCommands(['RUN echo "Hello, world!"'])
// Use an existing Dockerfileconst image = Image.fromDockerfile('Dockerfile')
// Extend an existing Dockerfileconst image = Image.fromDockerfile("app/Dockerfile").pipInstall(['numpy'])# Add custom Dockerfile commandsimage = Daytona::Image.debian_slim('3.12').dockerfile_commands(['RUN echo "Hello, world!"'])
# Use an existing Dockerfileimage = Daytona::Image.from_dockerfile('Dockerfile')
# Extend an existing Dockerfileimage = Daytona::Image.from_dockerfile('app/Dockerfile').pip_install(['numpy'])// Note: In Go, FromDockerfile takes the Dockerfile content as a stringcontent, err := os.ReadFile("Dockerfile")if err != nil { // handle error}image := daytona.FromDockerfile(string(content))
// Extend an existing Dockerfile with additional commandscontent, err = os.ReadFile("app/Dockerfile")if err != nil { // handle error}image := daytona.FromDockerfile(string(content)). PipInstall([]string{"numpy"})System package installation
Section titled “System package installation”Daytona provides an option to install OS-level packages during the image build. Use this pattern when your sandbox needs CLI tools or system libraries that are not available through pip.
Each string passed to run_commands becomes a separate Dockerfile RUN instruction, and every RUN produces an immutable layer. To keep the image small, chain the package install and the apt cache cleanup together with && inside a single string so the cache is never persisted in any layer.
image = Image.debian_slim("3.12").run_commands( "apt-get update " "&& apt-get install -y --no-install-recommends git curl ffmpeg jq " "&& rm -rf /var/lib/apt/lists/*")const image = Image.debianSlim('3.12').runCommands( 'apt-get update ' + '&& apt-get install -y --no-install-recommends git curl ffmpeg jq ' + '&& rm -rf /var/lib/apt/lists/*',)image = Daytona::Image .debian_slim('3.12') .run_commands( 'apt-get update ' \ '&& apt-get install -y --no-install-recommends git curl ffmpeg jq ' \ '&& rm -rf /var/lib/apt/lists/*' )version := "3.12"image := daytona.DebianSlim(&version). AptGet([]string{"git", "curl", "ffmpeg", "jq"})Image image = Image.debianSlim("3.12").runCommands( "apt-get update " + "&& apt-get install -y --no-install-recommends git curl ffmpeg jq " + "&& rm -rf /var/lib/apt/lists/*");Non-root user setup
Section titled “Non-root user setup”Daytona provides an option to define a non-root user for application workloads. Run all installation steps as root first, then create the user, fix ownership of the working directory, and switch to the new user with the USER directive. Subsequent commands and the sandbox runtime then operate without root privileges.
Place all installation steps before the USER directive. After switching to the non-root user, commands that write to system locations (such as apt-get install or pip install without --user) will fail with permission errors.
image = ( Image.debian_slim("3.12") .pip_install(["fastapi", "uvicorn"]) .run_commands( "groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona", "chown -R daytona:daytona /home/daytona", ) .workdir("/home/daytona") .dockerfile_commands(["USER daytona"]))const image = Image.debianSlim('3.12') .pipInstall(['fastapi', 'uvicorn']) .runCommands( 'groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona', 'chown -R daytona:daytona /home/daytona', ) .workdir('/home/daytona') .dockerfileCommands(['USER daytona'])image = Daytona::Image .debian_slim('3.12') .pip_install(['fastapi', 'uvicorn']) .run_commands( 'groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona', 'chown -R daytona:daytona /home/daytona' ) .workdir('/home/daytona') .dockerfile_commands(['USER daytona'])version := "3.12"image := daytona.DebianSlim(&version). PipInstall([]string{"fastapi", "uvicorn"}). Run("groupadd -r daytona && useradd -r -g daytona -m -d /home/daytona daytona"). Run("chown -R daytona:daytona /home/daytona"). Workdir("/home/daytona"). User("daytona")Multi-language runtimes
Section titled “Multi-language runtimes”Daytona provides an option to combine multiple language runtimes in a single image. The following pattern adds Node.js 20 to a Python base image by installing it from the NodeSource repository. The same approach works for adding Go, Ruby, Java, or any other runtime that distributes a Linux installer.
Chain the apt operations, the NodeSource installer, and the cache cleanup into a single RUN instruction. If the cache cleanup runs in a separate RUN, the apt cache is already persisted in the earlier layers and the final image keeps those bytes.
image = ( Image.debian_slim("3.12") .run_commands( "apt-get update " "&& apt-get install -y --no-install-recommends curl ca-certificates " "&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - " "&& apt-get install -y nodejs " "&& rm -rf /var/lib/apt/lists/*" ) .pip_install(["fastapi", "uvicorn"]))const image = Image.debianSlim('3.12') .runCommands( 'apt-get update ' + '&& apt-get install -y --no-install-recommends curl ca-certificates ' + '&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - ' + '&& apt-get install -y nodejs ' + '&& rm -rf /var/lib/apt/lists/*', ) .pipInstall(['fastapi', 'uvicorn'])image = Daytona::Image .debian_slim('3.12') .run_commands( 'apt-get update ' \ '&& apt-get install -y --no-install-recommends curl ca-certificates ' \ '&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - ' \ '&& apt-get install -y nodejs ' \ '&& rm -rf /var/lib/apt/lists/*' ) .pip_install(['fastapi', 'uvicorn'])version := "3.12"image := daytona.DebianSlim(&version). Run("apt-get update " + "&& apt-get install -y --no-install-recommends curl ca-certificates " + "&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - " + "&& apt-get install -y nodejs " + "&& rm -rf /var/lib/apt/lists/*"). PipInstall([]string{"fastapi", "uvicorn"})Image image = Image.debianSlim("3.12") .runCommands( "apt-get update " + "&& apt-get install -y --no-install-recommends curl ca-certificates " + "&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - " + "&& apt-get install -y nodejs " + "&& rm -rf /var/lib/apt/lists/*" ) .pipInstall("fastapi", "uvicorn");