apps/docs/src/content/docs/en/declarative-builder.mdx
import { TabItem, Tabs } from '@astrojs/starlight/components'
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:
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.
<Tabs syncKey="language"> <TabItem label="Python" icon="seti:python"># Define a declarative image with python packages
declarative_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 logs
sandbox = daytona.create(
CreateSandboxFromImageParams(image=declarative_image),
timeout=0,
on_snapshot_create_logs=print,
)
// Define a declarative image with python packages
const declarativeImage = Image.debianSlim('3.12')
.pipInstall(['requests', 'pytest'])
.workdir('/home/daytona')
// Create a new sandbox with the declarative image and stream the build logs
const sandbox = await daytona.create(
{
image: declarativeImage,
},
{
timeout: 0,
onSnapshotCreateLogs: console.log,
}
)
# Define a simple declarative image with Python packages
declarative_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 logs
sandbox = daytona.create(
Daytona::CreateSandboxFromImageParams.new(image: declarative_image),
on_snapshot_create_logs: proc { |chunk| puts chunk }
)
// Define a declarative image with python packages
version := "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 logs
logChan := 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 packages
Image declarativeImage = Image.debianSlim("3.12")
.pipInstall("requests", "pytest")
.workdir("/home/daytona");
// Create a new sandbox with the declarative image and stream the build logs
CreateSandboxFromImageParams params = new CreateSandboxFromImageParams();
params.setImage(declarativeImage);
Sandbox sandbox = daytona.create(params, 0L, System.out::println);
:::note Use the following best practices when working with the declarative builder:
Layer Optimization: Group related operations to minimize Docker layers
Cache Utilization: Identical build commands and context will be cached and subsequent builds will be almost instant
Security: Create non-root users for application workloads
Resource Efficiency: Use slim base images when appropriate
Context Minimization: Only include necessary files in the build context :::
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.
<Tabs syncKey="language"> <TabItem label="Python" icon="seti:python"># Create a python data science image
snapshot_name = "data-science-snapshot"
image = (
Image.debian_slim("3.12")
.pip_install(["pandas", "numpy"])
.workdir("/home/daytona")
)
# Create the snapshot and stream the build logs
daytona.snapshot.create(
CreateSnapshotParams(
name=snapshot_name,
image=image,
),
on_logs=print,
)
# Create a new sandbox using the pre-built snapshot
sandbox = daytona.create(
CreateSandboxFromSnapshotParams(snapshot=snapshot_name)
)
// Create a python data science image
const snapshotName = 'data-science-snapshot'
const image = Image.debianSlim('3.12')
.pipInstall(['pandas', 'numpy'])
.workdir('/home/daytona')
// Create the snapshot and stream the build logs
await daytona.snapshot.create(
{
name: snapshotName,
image,
},
{
onLogs: console.log,
}
)
// Create a new sandbox using the pre-built snapshot
const sandbox = await daytona.create({
snapshot: snapshotName,
})
# Create a simple Python data science image
snapshot_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 logs
daytona.snapshot.create(
Daytona::CreateSnapshotParams.new(
name: snapshot_name,
image: image
),
on_logs: proc { |chunk| puts chunk }
)
# Create a new Sandbox using the pre-built Snapshot
sandbox = daytona.create(
Daytona::CreateSandboxFromSnapshotParams.new(snapshot: snapshot_name)
)
// Create a python data science image
snapshotName := "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 snapshot
sandbox, err := client.Create(ctx, types.SnapshotParams{
Snapshot: snapshotName,
})
if err != nil {
// handle error
}
// Create a python data science image
String snapshotName = "data-science-snapshot";
Image image = Image.debianSlim("3.12")
.pipInstall("pandas", "numpy")
.workdir("/home/daytona");
// Create the snapshot and stream the build logs
daytona.snapshot().create(snapshotName, image, System.out::println);
// Create a new sandbox using the pre-built snapshot
CreateSandboxFromSnapshotParams snapshotParams = new CreateSandboxFromSnapshotParams();
snapshotParams.setSnapshot(snapshotName);
Sandbox sandbox = daytona.create(snapshotParams);
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.
Daytona provides an option to select base images. The following snippets demonstrate how to select and configure base images:
<Tabs syncKey="language"> <TabItem label="Python" icon="seti:python"># Create an image from a base
image = Image.base("python:3.12-slim-bookworm")
# Use a Debian slim image with Python 3.12
image = Image.debian_slim("3.12")
// Create an image from a base
const image = Image.base('python:3.12-slim-bookworm')
// Use a Debian slim image with Python 3.12
const image = Image.debianSlim('3.12')
# Create an image from a base
image = Daytona::Image.base('python:3.12-slim-bookworm')
# Use a Debian slim image with Python 3.12
image = Daytona::Image.debian_slim('3.12')
// Create an image from a base
image := daytona.Base("python:3.12-slim-bookworm")
// Use a Debian slim image with Python 3.12
version := "3.12"
image := daytona.DebianSlim(&version)
// Create an image from a base
Image image = Image.base("python:3.12-slim-bookworm");
// Use a Debian slim image with Python 3.12
image = Image.debianSlim("3.12");
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:
<Tabs syncKey="language"> <TabItem label="Python" icon="seti:python"># Add pip packages
image = Image.debian_slim("3.12").pip_install(["requests", "pandas"])
# Install from requirements.txt
image = 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 packages
const image = Image.debianSlim('3.12').pipInstall(['requests', 'pandas'])
// Install from requirements.txt
const 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']
})
image = Daytona::Image.debian_slim('3.12').pip_install_from_requirements('requirements.txt')
image = Daytona::Image.debian_slim('3.12').pip_install_from_pyproject('pyproject.toml', optional_dependencies: ['dev'] )
</TabItem>
<TabItem label="Go" icon="seti:go">
```go
// Add pip packages
version := "3.12"
image := daytona.DebianSlim(&version).PipInstall([]string{"requests", "pandas"})
// Install from requirements.txt
image := 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 packages
Image image = Image.debianSlim("3.12").pipInstall("requests", "pandas");
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:
<Tabs syncKey="language"> <TabItem label="Python" icon="seti:python"># Add a local file
image = Image.debian_slim("3.12").add_local_file("package.json", "/home/daytona/package.json")
# Add a local directory
image = Image.debian_slim("3.12").add_local_dir("src", "/home/daytona/src")
// Add a local file
const image = Image.debianSlim('3.12').addLocalFile('package.json', '/home/daytona/package.json')
// Add a local directory
const image = Image.debianSlim('3.12').addLocalDir('src', '/home/daytona/src')
# Add a local file
image = Daytona::Image.debian_slim('3.12').add_local_file('package.json', '/home/daytona/package.json')
# Add a local directory
image = Daytona::Image.debian_slim('3.12').add_local_dir('src', '/home/daytona/src')
// Add a local file
version := "3.12"
image := daytona.DebianSlim(&version).AddLocalFile("package.json", "/home/daytona/package.json")
// Add a local directory
image := daytona.DebianSlim(&version).AddLocalDir("src", "/home/daytona/src")
Daytona provides an option to configure environment variables and working directories. The following snippets demonstrate how to configure environment variables and working directories:
<Tabs syncKey="language"> <TabItem label="Python" icon="seti:python"># Set environment variables
image = Image.debian_slim("3.12").env({"PROJECT_ROOT": "/home/daytona"})
# Set working directory
image = Image.debian_slim("3.12").workdir("/home/daytona")
// Set environment variables
const image = Image.debianSlim('3.12').env({ PROJECT_ROOT: '/home/daytona' })
// Set working directory
const image = Image.debianSlim('3.12').workdir('/home/daytona')
# Set environment variables
image = Daytona::Image.debian_slim('3.12').env({ 'PROJECT_ROOT' => '/home/daytona' })
# Set working directory
image = Daytona::Image.debian_slim('3.12').workdir('/home/daytona')
// Set environment variables
version := "3.12"
image := daytona.DebianSlim(&version).Env("PROJECT_ROOT", "/home/daytona")
// Set working directory
image := daytona.DebianSlim(&version).Workdir("/home/daytona")
// Set environment variables
Image image = Image.debianSlim("3.12")
.env(java.util.Map.of("PROJECT_ROOT", "/home/daytona"));
// Set working directory
image = Image.debianSlim("3.12").workdir("/home/daytona");
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:
<Tabs syncKey="language"> <TabItem label="Python" icon="seti:python"># Run shell commands during build
image = 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 entrypoint
image = Image.debian_slim("3.12").entrypoint(["/bin/bash"])
# Set default command
image = Image.debian_slim("3.12").cmd(["/bin/bash"])
// Run shell commands during build
const 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 entrypoint
const image = Image.debianSlim('3.12').entrypoint(['/bin/bash'])
// Set default command
const image = Image.debianSlim('3.12').cmd(['/bin/bash'])
# Run shell commands during build
image = 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 entrypoint
image = Daytona::Image.debian_slim('3.12').entrypoint(['/bin/bash'])
# Set default command
image = Daytona::Image.debian_slim('3.12').cmd(['/bin/bash'])
// Run shell commands during build
version := "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 entrypoint
image := daytona.DebianSlim(&version).Entrypoint([]string{"/bin/bash"})
// Set default command
image := daytona.DebianSlim(&version).Cmd([]string{"/bin/bash"})
// Run shell commands during build
Image 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 entrypoint
image = Image.debianSlim("3.12").entrypoint("/bin/bash");
// Set default command
image = Image.debianSlim("3.12").cmd("/bin/bash");
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:
<Tabs syncKey="language"> <TabItem label="Python" icon="seti:python"># Add custom Dockerfile commands
image = Image.debian_slim("3.12").dockerfile_commands(["RUN echo 'Hello, world!'"])
# Use an existing Dockerfile
image = Image.from_dockerfile("Dockerfile")
# Extend an existing Dockerfile
image = Image.from_dockerfile("app/Dockerfile").pip_install(["numpy"])
// Add custom Dockerfile commands
const image = Image.debianSlim('3.12').dockerfileCommands(['RUN echo "Hello, world!"'])
// Use an existing Dockerfile
const image = Image.fromDockerfile('Dockerfile')
// Extend an existing Dockerfile
const image = Image.fromDockerfile("app/Dockerfile").pipInstall(['numpy'])
# Add custom Dockerfile commands
image = Daytona::Image.debian_slim('3.12').dockerfile_commands(['RUN echo "Hello, world!"'])
# Use an existing Dockerfile
image = Daytona::Image.from_dockerfile('Dockerfile')
# Extend an existing Dockerfile
image = Daytona::Image.from_dockerfile('app/Dockerfile').pip_install(['numpy'])
// Note: In Go, FromDockerfile takes the Dockerfile content as a string
content, err := os.ReadFile("Dockerfile")
if err != nil {
// handle error
}
image := daytona.FromDockerfile(string(content))
// Extend an existing Dockerfile with additional commands
content, err = os.ReadFile("app/Dockerfile")
if err != nil {
// handle error
}
image := daytona.FromDockerfile(string(content)).
PipInstall([]string{"numpy"})
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/*"
);
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")
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");