If you selected the ona-samples/workshop repo (recommended for first-timers), this lab builds on the configuration files already in that repository. The examples below reference that project’s structure.If you’re using your own repository, use the examples as a guide and adapt them to your project’s stack and needs.
Why configuration as code matters
Traditional development setup is manual and fragile:- “Install Node 18, then PostgreSQL, then Redis…”
- Works differently on Mac vs Windows vs Linux
- Drifts over time as people install different versions
- Takes hours for new team members
.devcontainer/devcontainer.json: Your tools and environment.ona/automations.yaml: Your tasks and services
Dev Containers: The foundation
How Dev Containers work
A Dev Container starts with a base image or Dockerfile, adds features (like Docker or Python), configures your IDE, and runs commands to complete setup.Understanding your Dev Container
Open.devcontainer/devcontainer.json in your IDE. If you’re using the ona-samples/workshop repo, it looks like this:
build: Instead of a pre-built image, this uses aDockerfilein the same directory. This gives you full control over the base environment. The Dockerfile in the workshop repo usesgitpod/workspace-full, which includes Node.js and common development tools.customizations.vscode.extensions: IDE extensions installed automatically. Here, ESLint and Prettier are included so every developer gets consistent linting and formatting.remoteUser/containerUser: The user identity inside the container. Usinggitpodensures correct file permissions.forwardPorts: Ports that are automatically forwarded from the environment to your IDE. Port 3000 is the frontend, 3001 is the backend API, and 5432 is for PostgreSQL.portsAttributes: Controls how forwarded ports behave (labels, auto-forward behavior).
Try it: Customize your Dev Container
Let’s add a useful tool to your environment. We’ll add the GitHub CLI as a Dev Container feature.- Open
.devcontainer/devcontainer.json - Add a
featuressection (if one doesn’t exist): - You can also add VS Code settings to enable format-on-save:
- Save the file
- Rebuild the container to apply your changes:
- VS Code / Cursor: Open the Command Palette (
Cmd+Shift+P/Ctrl+Shift+P) and run Ona: Rebuild Container - Terminal: Run
gitpod environment devcontainer rebuild - Browser IDE: Stop and restart the environment from the Ona dashboard
- VS Code / Cursor: Open the Command Palette (
- Verify: run
gh --versionin the terminal
Automations: Tasks and services
Dev Containers handle tools. Automations handle what runs in your environment.The two types of automations
Tasks: One-time commands (install dependencies, run migrations, seed data) Services: Long-running processes (databases, backend servers, dev servers)Understanding your automations
Open.ona/automations.yaml in your IDE. If you’re using the ona-samples/workshop repo, it contains a backend service:
services: Long-running processes that stay active in the background. Thebackendservice runs the Express API server.triggeredBy: postDevcontainerStart: The service starts automatically after the Dev Container is ready.commands.start: The sequence of commands to run: install dependencies, initialize the database, and start the server.commands.ready: A health check that confirms the service is running. Ona uses this to determine when dependent services can start.
npm install, no manual node server.js.
Try it: Add a frontend service
The workshop repo has a frontend app, but it’s not configured as an automation yet. Let’s add it. Open.ona/automations.yaml and add a frontend service:
backend and frontend services running. The frontend dev server is now available on port 3000.
Advanced: Service dependencies and tasks
Services can depend on each other, and tasks handle one-time operations. Here’s an example that extends the workshop setup:- Services: Long-running processes with
commands.startand an optionalcommands.readyhealth check. The ready check confirms the service is available before dependent tasks run. - Tasks: One-time commands with a single
commandfield. Theseed-datatask adds sample data to the database. Run it withgitpod automations task start seed-data.
AGENTS.md: Teaching agents about your project
Ona Agents can read code, but they don’t know your team’s conventions.AGENTS.md teaches them how your project works, what patterns to follow, and how to validate their changes.
What goes in AGENTS.md
CreateAGENTS.md in your repository root. Here’s an example based on the workshop project:
- Your architecture and framework choices
- Where to find and place code
- How to test and validate changes
- Your workflow for common tasks
Try it: Create AGENTS.md for your project
- Create
AGENTS.mdin your repository root - Add sections describing:
- What the project is (tech stack, architecture)
- Project structure (where things live)
- Coding conventions (patterns, naming)
- Common tasks agents might help with
- Keep it concise, 2-3 short bullet points per section
- Commit and push it
Troubleshooting
Dev Container won’t build- Validate JSON syntax: Copy your config into jsonlint.com
- Try a minimal config first, then add features incrementally
- Check feature compatibility: Some features require specific base images
- Check YAML syntax (whitespace matters!)
- View logs:
gitpod automations service logs <name> - Ensure Docker is available if your automation uses it (add docker-in-docker feature)
- Check that
commands.readysucceeds; test the command manually in your terminal
- Add port to
forwardPortsindevcontainer.json:"forwardPorts": [3000, 5432] - Or use CLI:
gitpod environment port open 3000 - Check if service is actually listening:
netstat -tulpn | grep <port>
- File must be named exactly
AGENTS.mdin repository root - Must be committed to Git (agents read from repository)
- Keep it focused - agents have context limits
- Tasks support
dependsOnto control execution order between tasks - Services wait for the
commands.readycheck to pass before dependent tasks run - Services wait for the
commands.readycheck to pass before dependents start - Check that your ready command actually succeeds: test it manually in the terminal
What you’ve learned
You now know how to:- Configure Dev Containers with base images, features, and IDE settings
- Create Automations for tasks (one-time commands) and services (long-running processes)
- Manage dependencies between services for correct startup order
- Write AGENTS.md to teach agents your project conventions
- Troubleshoot configuration issues
Need More Examples?
For a comprehensive library of ready-to-use configurations, see Examples Library:- Multiple Dev Container configurations for different stacks (Node, Python, Go, Rust, React)
- Complete Automation patterns (full-stack, monorepo, microservices, testing)
- AGENTS.md templates for common frameworks
- Slash command examples
- Complete workflow patterns
Next: Lab 3: Agents in Action