Configuration (oakapp.toml)
oakapp.toml file that contains static app's metadata and build steps. Metadata includes the app's identifier and version. Build steps are commands that are executed in the container to build or run the app. Example oakapp.toml file:Toml
1# (Required) App Identifier
2identifier = "com.luxonis.python_demo"
3# (Required) App Entrypoint
4entrypoint = ["bash", "-c", "python3 /app/main.py"]
5
6# (Optional) Prepare container commands
7# Here is the place where you can install all the dependencies that are needed at run-time
8prepare_container = [
9 { type = "COPY", source = "requirements.txt", target = "requirements.txt" },
10 { type = "RUN", command = "apt-get update" },
11 { type = "RUN", command = "apt-get install -y python3-pip" },
12 { type = "RUN", command = "pip3 install -r /app/requirements.txt --break-system-packages" },
13]
14
15# (Optional) Prepare build dependencies
16# Here is the place where you can install all the dependencies that are needed at build-time
17prepare_build_container = [
18 # Example: npm, gcc, ...
19]
20
21# (Optional) Additional commands after all the app files are copied to the container
22build_steps = []Configuration Fields
App Metadata
identifier- name of the app in Java-like format (with dots as separators; e.g.,com.my_company.my_app)com.luxonisnamespace is reserved for official appscom.examplenamespace is not recommended for production apps
app_version- version of the app in formatmajor.minor.patch(e.g.,1.0.0)
App Build and Runtime Configuration
entrypoint- array of strings (a command in argv format)cwd- (optional, default is/) current working directory for the containershell- (optional, default is["/bin/bash", "-c"]) shell to use for executingbuild_steps,entrypoint, andRUNcommands inprepare_containerandprepare_build_containersectionsapp_dir_name(optional/advanced; default isapp) - directory inside the container where the app files are copied; use only if you already use this directory name for something else
Shell Form vs Executable Form
Similarly to Docker, all the commands can be specified in executable form (array of strings) or in shell form (single string). For
entrypoint we strongly recommend using executable form.App Layers
- Base Image layer (specified in
base_imagesection) - Runtime layer (built in
prepare_containersection) - Build layer (built in
prepare_build_containersection; this layer is disconnected from runtime layer after build) - App layer (all the files from the app's directory and the
build_stepsare executed here)
Note
In Docker terminology, each of these layers corresponds to a single
RUN instruction in a Dockerfile.This allows for efficient caching while avoiding common pitfalls.- Static Frontend layer (if specified in
static_frontendsection) - DepthAI Models Cache layer (if specified in
depthai_modelssection)
Base Image
debian/bookworm-slim, pulled from registry-1.docker.io. The full base_image configuration looks like this:Toml
1[base_image]
2api_url = "https://registry-1.docker.io" # Optional: Service https address
3service = "registry.docker.io" # Optional: Name of Image Registry Service
4image_name = "library/debian"
5image_tag = "bookworm-slim"
6
7oauth_url = "https://auth.docker.io/token" # Optional: address to oauth 2.0 token for this service
8auth_type = "repository" # Optional: scope type
9auth_name = "library/debian" # Optional: name of resourceRecommendation
For DepthAI OAK Apps, we recommend our Base Image that includes most of the dependencies you may need.
Using a Custom Base Image
base_image to your own image. This is useful when you want to iterate on OS packages, runtimes, or shared dependencies in a familiar Docker workflow first, and then package the final app with oakapp.toml.This can also be a good fit when you are developing a reusable base environment and want to share it across multiple apps. Use a custom base image for stable, shared environment setup, and use prepare_container and prepare_build_container for app-specific runtime and build steps.To use a different image as the base layer, define it in oakapp.toml like this:Toml
1[base_image]
2image_name = "myorg/my-base"
3image_tag = "latest"base_image to a different registry using the api_url field.Docker Hub unauthenticated pulls
oak-agent currently does unauthenticated pulls from Docker Hub. After repeated pulls, or when building from a shared network, Docker Hub may reject the request because of its anonymous pull rate limit.To avoid this, see Self-Hosting a Container Registry.If that happens, the build can fail with an error similar to this:Sh
1oakctl command failed:
2 0: Received an error RPC response: Failed to build application container:
3 Image download failed: OCI registry client received an error:
4 APIError(ErrorList { errors: [Error { code: "TOOMANYREQUESTS", message:
5 "You have reached your unauthenticated pull rate limit.
6 https://www.docker.com/increase-rate-limit", detail: Null }] })Self-Hosting a Container Registry
base_image.api_url to it during development.Run a registry that is reachable from the device:Command Line
1docker run --rm -d -p 5000:5000 --name registry registry:2Sh
1echo "FROM ubuntu:26.04" > ./Dockerfile
2docker buildx build --platform linux/arm64 -t myapp-base:latest --load .
3docker tag myapp-base:latest localhost:5000/myapp-base:latest
4docker push localhost:5000/myapp-base:latest[base_image] section in oakapp.toml like this:Toml
1[base_image]
2api_url = "http://<self-hosted-registry-ip>:5000" # Service http address visible from the device
3image_name = "myapp-base"
4image_tag = "latest"base_image.api_url has to point to the device's network-accessible address. For the uploaded image to work on an OAK4 device it has to be uploaded as a linux/arm64 image.Runtime And Build Layer
prepare_container and prepare_build_container are arrays of commands that are executed in the container to prepare the respective layers. The only difference is that the build layer is not present at runtime.The available command types are:RUN- run a command in the container
Toml
1prepare_container = [
2 { type = "RUN", command = "apt-get update" },
3 { type = "RUN", command = "apt-get install -y python3-pip" },
4]COPY- copy a file from the app's directory to the container sooner than in the App layer. This is useful for copying dependency files likerequirements.txt. It is not necessary to copy the actual app files here, as they are copied later in the App layer.source- path to the source file relative to the app's directory on the hosttarget- path to app directory inside the container (relative toapp_dir_nameif specified)
Toml
1prepare_container = [
2 { type = "COPY", source = "requirements.txt", target = "requirements.txt" },
3]App Layer
app_dir_name if specified). Then, the commands in build_steps are executed in order.build_steps- steps to build an application or other preparation commands to be executed (array of strings)
Toml
1build_steps = [
2 "g++ -o /app/my_app /app/main.cpp",
3 "chmod +x /app/my_app",
4].oakappignore file to your app's directory. The syntax is similar to .gitignore. Example:Plain Text
1# Ignore all .log files
2*.log
3# Ingore node_modules/ directory
4node_modules/Mounts
required_mounts,required_devices- mounts that are required - if not present on startup of container, the app is stopped with error
source[:target[:options]]; defaults:- source=target
- options="rbind,rw"
- All mount paths must be absolute. Relative paths are rejected for
required_mounts,required_devices,optional_mounts,optional_devices, andadditional_mounts(source/target).
Toml
1required_mounts = [
2 "/data/my-storage:/app/storage:rw,rbind",
3]optional_mounts,optional_devices- if not present of startup of container, these mounts are ignoredadditional_mounts(source,target,type,options,required) array of objects, see below for example:
Toml
1additional_mounts = [
2 { source = "/run/user/1000/pulse", target = "/run/user/1000/pulse", type = "none", options = [
3 "rbind",
4 "rw",
5 ], required = true },
6 { source = "/home/root/.config/pulse/cookie", target = "/root/.config/pulse/cookie", type = "none", options = [
7 "bind",
8 "ro",
9 ], required = true },
10]additional_build_mounts- same asadditional_mounts, but only for build time; these mounts are not present at runtimeallowed_devices- list of devices that the app is allowed to access inside the container
Toml
1allowed_devices = [{ allow = true, access = "rwm" }]Static frontend
static_frontenddist_path- path to the built frontend files (relative to the app's directory on the host)build(optional) - build configuration for the frontendsource_path- path to the frontend source filessteps- array of commands to build the frontend
Toml
1[static_frontend]
2dist_path = "./frontend/dist" # path to the built frontend files
3
4[static_frontend.build]
5source_path = "./frontend" # path to the frontend source files
6steps = ["cd /app/frontend/src && npm install && npm run build"]assign_frontend_port- if set to true, an available port will be assigned toOAKAPP_STATIC_FRONTEND_PORTenvironment variable inside the container; this can be used even ifstatic_frontendis not specified
Toml
1assign_frontend_port = truestatic_frontend_dir_name(optional/advanced; default isstatic_frontend) - directory inside the container where the static frontend files are mounted (will be stored inOAKAPP_STATIC_FRONTEND_PATHenvironment variable); use only if you already use this directory name for something else
DepthAI Models Cache
*.yaml format that will be cached inside the container during build time. This way, you can create apps that work offline without the need to download models at runtime.depthai_modelsyaml_path- path to the DepthAI models folder in*.yamlformatuse_only_cache- if set to true, only the cached models will be used, and no new models will be downloaded at runtime (setsDEPTHAI_ZOO_INTERNET_CHECKenvironment variable)
Toml
1depthai_models = { yaml_path = "./backend/src/depthai_models" }depthai_models_dir_name(optional/advanced; default isdepthai_models) - directory inside the container where the DepthAI models are mounted; use only if you already use this directory name for something else
Environment Variables and Build Arguments
Toml
1[env]
2MY_ENV_VAR = "some_value"
3ANOTHER_ENV_VAR = "another_value"Toml
1[arg]
2MY_BUILD_ARG = "some_build_value"
3ANOTHER_BUILD_ARG = "another_build_value"oakctl app run --env MY_ENV_VAR=value).