Devfiles and Workspace Base Images
What is a Devfile?
A devfile is a YAML file (devfile.yaml) placed at the root of a Git repository that declares the complete workspace environment: which container image to use, what ports to expose, what commands to run, and what volumes to mount.
When a developer creates a workspace from a Git repository, Dev Spaces reads the devfile and builds the workspace pod accordingly. If no devfile is found, Dev Spaces falls back to the Universal Developer Image (UDI) with default settings.
Devfile Structure
A devfile has the following top-level sections:
schemaVersion: 2.2.0
metadata:
name: my-project
attributes: {} (1)
components: [] (2)
commands: [] (3)
events: {} (4)
| 1 | IDE-specific settings like recommended extensions |
| 2 | Containers, volumes, and other components |
| 3 | Build, run, and test commands available from the IDE |
| 4 | Lifecycle hooks (preStart, postStart, preStop, postStop) |
Components
Components define the containers and volumes in your workspace.
Container Component
components:
- name: tools
container:
image: quay.io/devfile/universal-developer-image:ubi9-latest
memoryLimit: 5Gi
cpuLimit: 2500m
env:
- name: QUARKUS_HTTP_HOST
value: "0.0.0.0"
- name: MAVEN_OPTS
value: "-Dmaven.repo.local=/home/user/.m2/repository"
volumeMounts:
- name: m2
path: /home/user/.m2
endpoints:
- name: app
targetPort: 8080
exposure: public
protocol: https
- name: debug
targetPort: 5005
exposure: none
Commands
Commands define tasks available in the IDE’s command palette. Think of these as shortcuts for executing commands without having to memorize them:
commands:
- id: package
exec:
label: "1. Package the application"
component: tools
commandLine: "./mvnw package"
group:
kind: build
isDefault: true
- id: start-dev
exec:
label: "2. Start Development mode"
component: tools
commandLine: "./mvnw compile quarkus:dev"
group:
kind: run
isDefault: true
Events
Events let you run commands at workspace lifecycle points. A common pattern is to run initialization after the workspace starts:
events:
postStart:
- init-continue
This runs the command with id: init-continue after the workspace container starts, which can be used to generate configuration files for IDE extensions.
IDE Extensions
Specify recommended VS Code extensions by adding an extensions.json attribute.
When the workspace opens, the IDE will suggest these extensions for installation:
attributes:
.vscode/extensions.json: |
{
"recommendations": [
"redhat.java",
"redhat.vscode-quarkus",
"redhat.vscode-yaml",
"continue.continue"
]
}
Extension IDs come from the Open VSX registry (https://open-vsx.org).
Find the extension page and convert the URL path from namespace/extension to namespace.extension.
To force extensions to install automatically (rather than just recommend them), add a .vscode/extensions.json file directly to your Git repository with the same format.
When Dev Spaces clones the repo and opens the workspace, the IDE will pick up the file from the repo and install the listed extensions without prompting.
Universal Developer Image (UDI)
The UDI (quay.io/devfile/universal-developer-image:ubi9-latest) is a batteries-included container image with:
-
Languages/Runtimes: Java, Node.js, Python, Go, Ruby, C/C++, PHP, .NET
-
Build Tools: Maven, Gradle, npm, pip, cargo
-
Container Tools: Podman, Buildah, Skopeo
-
CLI Tools: oc, kubectl, git, curl, jq, yq
For most use cases, UDI is the right choice. Build a custom image only if you need to significantly reduce image size, add proprietary tools, or enforce a specific security posture.
Parent Devfiles
A devfile can inherit from a parent to avoid duplication across multiple repositories. This is useful when your organization has a standard base environment:
schemaVersion: 2.2.0
metadata:
name: parasol-insurance
parent:
uri: https://gitlab.example.com/devfiles/-/raw/main/devfile.yaml
The child devfile inherits all components, commands, and events from the parent and can override or extend them. This keeps individual project devfiles minimal while centralizing your standard configuration.
Complete Example
Here is a full Quarkus devfile with UDI, Maven caching, endpoint definitions, and Continue AI assistant integration:
schemaVersion: 2.2.0
metadata:
name: quarkus-workspace
components:
- name: development-tooling
container:
image: quay.io/devfile/universal-developer-image:ubi9-latest
env:
- name: QUARKUS_HTTP_HOST
value: "0.0.0.0"
- name: MAVEN_OPTS
value: "-Dmaven.repo.local=/home/user/.m2/repository"
memoryLimit: 5Gi
cpuLimit: 2500m
volumeMounts:
- name: m2
path: /home/user/.m2
endpoints:
- name: quarkus-dev
targetPort: 8080
exposure: public
protocol: https
- name: debug
targetPort: 5005
exposure: none
- name: tests
targetPort: 8081
exposure: none
- name: m2
volume:
size: 1G
commands:
- id: package
exec:
label: "1. Package the application"
component: development-tooling
commandLine: "./mvnw package"
group:
kind: build
isDefault: true
- id: start-dev
exec:
label: "2. Start Development mode (Hot reload + debug)"
component: development-tooling
commandLine: "./mvnw compile quarkus:dev"
group:
kind: run
isDefault: true
- id: init-continue
exec:
label: "Initialize Continue config"
component: development-tooling
workingDir: /home/user
commandLine: |
mkdir -p /home/user/.continue
cat > /home/user/.continue/config.yaml << 'INNEREOF'
name: Continue Config
version: 0.0.1
models:
- name: qwen3-14b
provider: vllm
model: qwen3-14b
apiKey: ${LLM_API_KEY}
apiBase: ${LLM_BASE_URL}
roles:
- chat
INNEREOF
group:
kind: build
events:
postStart:
- init-continue
The init-continue command reads LLM_API_KEY and LLM_BASE_URL from the environment variables injected by the shared secret created in the previous section.
Next Steps
Proceed to Application Modernization Overview.