Dev Containers (Development Containers) are a standardized way to define development environments using Docker containers. They allow you to:
- Package your entire development environment (tools, libraries, dependencies)
- Share consistent environments across teams
- Run applications in isolated, reproducible containers
- Use any Docker image or build custom environments
In Verily Workbench, devcontainers enable you to create custom cloud apps that run in isolated containers on GCP VMs.
┌─────────────────────────────────────┐
│ Workbench VM (GCE Instance) │
│ │
│ ┌───────────────────────────────┐ │
│ │ Docker Container │ │
│ │ (Your Custom App) │ │
│ │ │ │
│ │ - Application Server │ │
│ │ - Port: 8888 (or custom) │ │
│ │ - Network: app-network │ │
│ └───────────────────────────────┘ │
│ │
│ ┌───────────────────────────────┐ │
│ │ app-network (bridge) │ │
│ │ (External Docker network) │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
-
.devcontainer.json- Main configuration file- Defines Docker image, features, ports, commands
- Specifies post-creation and startup scripts
-
docker-compose.yaml- Docker Compose configuration- Defines the container service
- Sets up volumes, networks, ports
- Configures security settings
-
devcontainer-template.json- Template metadata- Defines template options (cloud provider, login, etc.)
- Used by Workbench UI to show template options
MUST be application-server:
container_name: "application-server"MUST use external network app-network:
networks:
- app-network
networks:
app-network:
external: truePort must be exposed on 0.0.0.0 (localhost):
ports:
- "8888:8888"If you need to mount workspace buckets, add:
cap_add:
- SYS_ADMIN
devices:
- /dev/fuse
security_opt:
- apparmor:unconfinedInclude Workbench startup scripts:
{
"postCreateCommand": [
"./startupscript/post-startup.sh",
"username",
"/home/username",
"${templateOption:cloud}",
"${templateOption:login}"
],
"postStartCommand": [
"./startupscript/remount-on-restart.sh",
"username",
"/home/username",
"${templateOption:cloud}",
"${templateOption:login}"
]
}Purpose: Analyze data from GCS buckets and generate comprehensive profiling reports
Location: src/lab-results-analyzer-dev/
Key Files:
- Image:
jupyter/scipy-notebook - Port:
8888 - User:
jovyan - Features:
- JupyterLab web interface
- Python data science stack (pandas, numpy, matplotlib, seaborn)
- Auto-installs
ydata-profilingandgoogle-cloud-storage
docker-compose.yaml:
services:
app:
container_name: "application-server"
image: "jupyter/scipy-notebook"
user: "jovyan"
restart: always
volumes:
- .:/workspace:cached
- work:/home/jovyan/work
ports:
- 8888:8888
networks:
- app-network
cap_add:
- SYS_ADMIN
devices:
- /dev/fuse
security_opt:
- apparmor:unconfined
command: "start-notebook.sh --ip=0.0.0.0 --port=8888 --no-browser --NotebookApp.token='' --NotebookApp.password='' --NotebookApp.allow_origin='*'"Use Case:
- Load CSV files from GCS data collections
- Generate automatic data profiling reports
- Analyze any dataset structure without hardcoding columns
Purpose: Run R statistical analysis with RStudio interface
Location: src/r-analysis/
Key Files:
- Image:
ghcr.io/rocker-org/devcontainer/tidyverse:4.4 - Port:
8787(RStudio default) - User:
rstudio - Features:
- RStudio Server web interface
- Tidyverse packages pre-installed
- Full R statistical computing environment
docker-compose.yaml:
services:
app:
container_name: "application-server"
image: "ghcr.io/rocker-org/devcontainer/tidyverse:4.4"
restart: always
volumes:
- .:/workspace:cached
- work:/home/rstudio:cached
ports:
- "8787:8787"
environment:
"DISABLE_AUTH": "true"
networks:
- app-network
cap_add:
- SYS_ADMIN
devices:
- /dev/fuse
security_opt:
- apparmor:unconfinedUse Case:
- Statistical analysis with R
- Data visualization with ggplot2
- R Shiny app development
- Bioinformatics analysis with R packages
Purpose: Full IDE experience in the browser
Location: src/vscode/
Key Files:
- Image: Custom VS Code Server image
- Port:
8080 - User:
vscode - Features:
- Full VS Code interface in browser
- Terminal access
- Extension support
- Git integration
Use Case:
- Code development and editing
- Full IDE features without local installation
- Multi-language support (Python, JavaScript, Go, etc.)
Purpose: Lightweight Linux environment with web-based terminal
Location: src/ubuntu-example/
Key Files:
- Image:
mcr.microsoft.com/devcontainers/base:ubuntu - Port:
7681 - User:
vscode - Features:
- ttyd web terminal
- Full Ubuntu environment
- Minimal overhead
docker-compose.yaml:
services:
app:
container_name: "application-server"
image: "mcr.microsoft.com/devcontainers/base:ubuntu"
user: vscode
restart: always
volumes:
- .:/workspace:cached
ports:
- "7681:7681"
networks:
- app-network
cap_add:
- SYS_ADMIN
devices:
- /dev/fuse
security_opt:
- apparmor:unconfined
command: ["ttyd", "-W", "-p", "7681", "bash"]Use Case:
- Command-line tools and scripts
- System administration tasks
- Lightweight development environment
- Custom tool installation
Purpose: Deep learning and AI model training with GPU support
Location: src/nemo_jupyter/
Key Files:
- Image: Custom Dockerfile with NVIDIA CUDA
- Port:
8888 - User:
jupyter - Features:
- NVIDIA GPU support
- CUDA toolkit
- PyTorch, TensorFlow
- JupyterLab interface
docker-compose.yaml:
services:
app:
container_name: "application-server"
build:
context: .
target: nemo
user: jupyter
restart: always
volumes:
- .:/workspace:cached
ports:
- "8888:8888"
networks:
- app-network
cap_add:
- SYS_ADMIN
devices:
- /dev/fuse
security_opt:
- apparmor:unconfined
command: jupyter lab --ip=0.0.0.0 --port=8888 --no-browser --LabApp.token=''Use Case:
- Deep learning model training
- Large language model fine-tuning
- GPU-accelerated computations
- AI/ML research and development
Purpose: Genomics analysis with NVIDIA Parabricks tools
Location: src/workbench-jupyter-parabricks/
Key Files:
- Image: Custom Dockerfile with Parabricks
- Port:
8888 - Features:
- NVIDIA Parabricks genomics tools
- GPU-accelerated DNA/RNA analysis
- JupyterLab for analysis notebooks
Use Case:
- DNA sequencing analysis
- Variant calling
- Genomics pipeline execution
- Bioinformatics research
Use the provided script:
./scripts/create-custom-app.sh <app-name> <docker-image> <port> [username] [home-dir]Example:
./scripts/create-custom-app.sh my-app jupyter/base-notebook 8888 jovyan /home/jovyanThis generates:
.devcontainer.jsondocker-compose.yamldevcontainer-template.jsonREADME.md
-
Create directory:
src/my-custom-app/ -
Create
.devcontainer.json:
{
"name": "my-custom-app",
"dockerComposeFile": "docker-compose.yaml",
"service": "app",
"shutdownAction": "none",
"workspaceFolder": "/workspace",
"postCreateCommand": [
"./startupscript/post-startup.sh",
"jovyan",
"/home/jovyan",
"${templateOption:cloud}",
"${templateOption:login}"
],
"features": {
"ghcr.io/devcontainers/features/java:1": {
"version": "17"
}
},
"remoteUser": "root"
}- Create
docker-compose.yaml:
services:
app:
container_name: "application-server"
image: "your-docker-image:tag"
user: "your-user"
restart: always
volumes:
- .:/workspace:cached
- work:/home/your-user/work
ports:
- "8888:8888"
networks:
- app-network
cap_add:
- SYS_ADMIN
devices:
- /dev/fuse
security_opt:
- apparmor:unconfined
volumes:
work:
networks:
app-network:
external: true- Create
devcontainer-template.json:
{
"id": "my-custom-app",
"version": "1.0.0",
"name": "my-custom-app",
"description": "My custom application",
"options": {
"cloud": {
"type": "string",
"enum": ["gcp", "aws"],
"default": "gcp"
}
}
}Dev Container Features are reusable components you can add to any container:
- Java:
"features": {
"ghcr.io/devcontainers/features/java:1": {
"version": "17"
}
}- Google Cloud CLI:
"features": {
"ghcr.io/dhoeric/features/google-cloud-cli:1": {}
}- AWS CLI:
"features": {
"ghcr.io/devcontainers/features/aws-cli:1": {}
}- ttyd (Web Terminal):
"features": {
"ghcr.io/ar90n/devcontainer-features/ttyd:1": {}
}- Jupyter:
"features": {
"ghcr.io/devcontainers/features/jupyter:1": {
"installJupyterlab": true
}
}- R Packages:
"features": {
"ghcr.io/rocker-org/devcontainer-features/r-packages:1": {
"packages": "shiny,shinydashboard",
"installSystemRequirements": true
}
}-
Fork the repository:
https://github.com/verily-src/workbench-app-devcontainers -
Add your app to
src/your-app-name/ -
Commit and push to your fork
-
In Workbench UI:
- Go to Apps → Create App → Custom
- Enter your repository URL
- Specify the template path:
src/your-app-name - Configure options (cloud provider, etc.)
- Deploy!
- Use appropriate base images: Choose images that match your use case
- Set correct user permissions: Use non-root users when possible
- Include startup scripts: Use Workbench's post-startup scripts
- Document your app: Add clear README.md files
- Test locally: Use
devcontainer upto test before deploying - Version your images: Use specific tags, not
latest - Minimize image size: Use multi-stage builds when possible
- Handle credentials securely: Use Workbench's credential management
- Main Repository: https://github.com/verily-src/workbench-app-devcontainers
- Dev Container Spec: https://containers.dev/
- Workbench Docs: https://support.workbench.verily.com/docs/guides/cloud_apps/create_custom_apps/
- Dev Container Features: https://containers.dev/features
Workbench devcontainers provide a powerful way to:
- ✅ Create custom cloud applications
- ✅ Share consistent environments
- ✅ Use any Docker image or build custom ones
- ✅ Access GCS buckets and workspace resources
- ✅ Run web-based applications (Jupyter, RStudio, VS Code, etc.)
- ✅ Support GPU-enabled workloads
- ✅ Integrate with Workbench's infrastructure
The key is following Workbench's specific requirements (container name, network, ports) while leveraging the flexibility of Docker and devcontainer features!