Recipe Workflow
Understanding how a Recipe is built and executed in DevZero.
Overview
When you create a Recipe on the DevZero Dashboard, it undergoes multiple stages before launching a workspace. These stages ensure that your development environment is correctly configured and optimized for performance.
Execution Stages
Recipes execute in three primary stages:
- Build-time: When a Recipe is saved and validated, generating a container image.
- Launch-time: When a workspace starts, running initial setup commands.
- Run-time: While the workspace is active, allowing interactive execution of commands.
Irrespective of the stage, command blocks will be executed in order, meaning later commands depend on the successful completion of previous ones.
Build Process
Once a Recipe is created, DevZero's system converts it into an LLB (Low-Level Build definition) and processes it in several steps:
1. Recipe to LLB Conversion
- Environment variables are injected (if applicable).
- Secrets stored in Vault are referenced securely.
- Commands are encoded and prepared for execution.
2. Build Execution in buildqd
Workers
- LLBs are passed to
buildkitd
for structured execution. - Secrets are securely retrieved and injected.
- Caching accelerates repeat builds, reducing setup time.
- Logs are streamed to the Web UI and CLI for monitoring.
3. Final Image Creation & Storage
- The built image is pushed to a team-specific OCI image registry.
- Cached layers improve performance for subsequent builds.
How It Works
Build-Time
The build phase is responsible for preparing and caching the workspace environment before it is launched. Every build step becomes a layer in the workspace container image, optimizing performance.
Each command block can be thought of as a layer in a Docker image. They are wrapped in a script and executed within a bash context. If directory
is specified:
- If absolute, it will be used as is.
- If relative, it will be relative to
/home/devzero
. - If unspecified, it defaults to
/home/devzero
.
Use Build-Time for:
ā Installing dependencies (apt-get
, dnf
, npm install
, etc.)
ā Setting up libraries or runtimes (Python, Node.js, Docker)
ā Copying files or setting up directories
Example:
Best Practices for Build-Time
- ā Install dependencies at build-time to reduce startup delays.
- ā Keep frequently updated packages near the end of the steps to preserve caching.
- ā
Use absolute paths when referencing binaries (
/usr/bin/python3
instead ofpython3
). - ā Do not add interactive prompts (use
-y
for package installations). - ā Do not start background services (
systemctl start
). These should be part of launch-time.
If a lower-layer build step changes, all subsequent steps will be rebuilt. Modify lower layers cautiously to retain cache efficiency.
Environment variables set via export
are not retained across steps. Write
them to a file or /etc/environment
instead.
If using a non-Debian-based OS, replace apt-get
with: - yum
for
CentOS/RHEL - dnf
for Fedora - apk
for Alpine Linux
Do not use build steps for executing daemonized processes (e.g.,
systemctl start ...
). Operations like IDE indexing should be in the
launch-time stage.
Launch-Time
Once a workspace starts, launch-time steps execute essential startup configurations. These steps are not cached and run every time a workspace is initialized.
Use Launch-Time for:
- Starting services (e.g.,
docker
,postgresql
) - Configuring access permissions
- Running setup scripts
Example:
Best Practices for Launch-Time
- ā
Use
systemctl start
for services instead of installing them at launch. - ā Limit heavy operations (installing large packages belongs in build-time).
- ā Ensure user permissions are properly set.
Launch-time commands cannot be interactive. If a step requires user input, it will get stuck in the logs.
Cacheable steps that make filesystem updates should be in the build-time stage. Binaries and files that users need immediately should be pre-installed.
Run-Time
Run-time steps execute while the workspace is active, meaning users can manually execute commands in a running workspace.
Use Run-Time for:
- Setting user-specific environment variables
- Running scripts dynamically based on user actions
- Configuring local workspace preferences
Example:
Unlike build-time and launch-time, run-time configurations are not predefined in the Recipe but instead happen dynamically while users interact with their workspaces.
User secrets cannot be accessed during build time. However, all user secrets are automatically available as environment variables within each user's workspace.
Final Notes
- Build-time is for installing dependencies & caching.
- Launch-time is for starting services & configuring settings.
- Run-time is for interactive environment adjustments.
Each stage plays a specific role in optimizing workspace performance while ensuring flexibility.