Administrator Operations Guide
This guide covers the deployment and management of the DDEV Coder template for administrators.
Prerequisites
Before deploying this template, ensure the following are in place:
Coder Server
- Coder v2+ server installed and running
- Administrative access to Coder
- Coder CLI installed and authenticated (
coder login <url>)
Setting up a new server? See the Server Setup Guide for step-by-step installation of Docker, Sysbox, and Coder.
Docker Host Infrastructure
- Sysbox runtime installed on all Coder agent nodes:
# Install prerequisites sudo apt-get install -y jq # Download Sysbox CE package (check https://github.com/nestybox/sysbox/releases for latest version) SYSBOX_VERSION=0.6.7 wget https://downloads.nestybox.com/sysbox/releases/v${SYSBOX_VERSION}/sysbox-ce_${SYSBOX_VERSION}-0.linux_amd64.deb # Install the package (note: sysbox-ce is not in standard apt repos) sudo apt-get install -y ./sysbox-ce_${SYSBOX_VERSION}-0.linux_amd64.deb # Verify installation sudo systemctl status sysbox -n20 sysbox-runc --versionNote: Docker must be installed (not via snap) before installing Sysbox. The installer will restart Docker. See Sysbox install docs for details.
- Docker configured to use Sysbox runtime for appropriate containers
- Sufficient storage for Docker volumes (each workspace uses dedicated
/var/lib/dockervolume)
Docker Registry Access
- Access to push images (Docker Hub or private registry)
- Registry credentials configured if using private registry
- For Docker Hub:
docker login
Local Tools
- Docker installed locally for building images
- Coder CLI installed and configured
- Git for version management
- Make (
sudo apt-get install -y makeon Ubuntu, pre-installed on macOS)
Building the Docker Image
The base image contains Ubuntu, Docker daemon, DDEV, Node.js, and essential development tools.
Using the Makefile
The Makefile automates all build and deployment tasks:
# Show available commands
make help
# Build image with cache
make build
# Build without cache (clean build)
make build-no-cache
# Push to registry
make push
# Build and push in one step
make build-and-push
# Test the built image
make test
# Show version info
make info
See image/README.md for details on customizing the Docker image.
Using GitHub Actions (push-image workflow)
The repository has a manually triggered workflow (.github/workflows/push-image.yml) that builds and pushes the image to Docker Hub from GitHub’s infrastructure. This is the preferred approach for official releases.
Prerequisites — configure once in GitHub repository settings:
- Secret
PUSH_SERVICE_ACCOUNT_TOKEN— 1Password service account token (from thepush-secretsvault) - Variable
DOCKERHUB_USERNAME— Docker Hub username (e.g.ddev)
The workflow reads DOCKERHUB_TOKEN from 1Password at op://push-secrets/DOCKERHUB_TOKEN/credential using the service account token.
To trigger a push:
- Update
VERSIONand commit/merge to the branch you want to build from. - Go to Actions → Push Image → Run workflow in the GitHub UI, select the branch, and click Run workflow.
- The workflow builds
linux/amd64, tags the image as bothddev/coder-ddev:<version>andddev/coder-ddev:latest, and pushes to Docker Hub.
Alternatively, trigger via the CLI:
gh workflow run push-image.yml --ref <branch>
Deploying the Template
Using the Makefile
# Push all four templates (no image build — use when only HCL changed)
make push-all-templates
# Push a single template
make push-template-user-defined-web
make push-template-drupal-core
make push-template-drupal-contrib
make push-template-freeform
# Full deployment: build image, push image, push all templates
make deploy-all
# Full deployment without cache (clean image build)
make build-and-push-no-cache && make push-all-templates
Auto-stop TTL
Set a default auto-stop so idle workspaces shut down and free resources. Run once after initial deployment (or after adding a new template):
coder templates edit drupal-core --default-ttl 2h --yes
coder templates edit drupal-contrib --default-ttl 2h --yes
coder templates edit user-defined-web --default-ttl 2h --yes
coder templates edit freeform --default-ttl 2h --yes
Users can override the TTL on their individual workspaces if needed.
Template Configuration
The template is defined in user-defined-web/template.tf. Key configuration parameters:
variable "workspace_image_registry" {
default = "index.docker.io/ddev/coder-ddev"
}
variable "image_version" {
default = "v0.1" # Update this when releasing new image versions
}
variable "cpu" {
default = 4 # CPU cores per workspace
}
variable "memory" {
default = 8 # RAM in GB per workspace
}
variable "docker_gid" {
default = 988 # Docker group ID (must match host)
}
To use a private registry:
- Update
workspace_image_registryintemplate.tf - Configure
registry_usernameandregistry_passwordvariables - Push template:
coder templates push --directory user-defined-web user-defined-web --yes
Version Management
Version Files
The VERSION file in the root directory controls the image tag. The Makefile automatically copies it into the template directory before pushing, and template.tf reads it from there — no manual edits to template.tf are needed.
Releasing a New Version
# 1. Update VERSION file
echo "v0.7" > VERSION
# 2. Build, push image, and push template (VERSION is synced automatically)
make deploy-user-defined-web
# Or without cache for clean build
make deploy-user-defined-web-no-cache
Managing Workspaces
Creating Workspaces
Via Web UI:
- Log into Coder dashboard
- Click “Create Workspace”
- Select “user-defined-web” template
- Enter workspace name
- Configure parameters (optional: CPU, memory)
- Click “Create Workspace”
Via CLI:
# Create with defaults
coder create --template user-defined-web my-workspace --yes
# Create with custom parameters
coder create --template user-defined-web my-workspace \
--parameter cpu=8 \
--parameter memory=16 \
--yes
Listing Workspaces
# List all workspaces
coder list
# List workspaces for specific template
coder list --template user-defined-web
# Show detailed workspace info
coder show my-workspace
Starting/Stopping Workspaces
# Stop workspace (saves state, stops billing)
coder stop my-workspace
# Start workspace
coder start my-workspace
# Restart workspace
coder restart my-workspace
Updating Workspaces
When you push a new template version, existing workspaces don’t automatically update.
To update a workspace to new template version:
# Update in place (preserves /home/coder)
coder update my-workspace
# Or from web UI: Click workspace → Update button
Notes:
- Updates template configuration but NOT the Docker image
- To update Docker image, workspace must be rebuilt (delete and recreate)
- Updating preserves
/home/codervolume - DDEV containers may need to be restarted after update
Deleting Workspaces
# Delete workspace (warns if running)
coder delete my-workspace
# Force delete
coder delete my-workspace --yes
# Delete multiple workspaces
for ws in workspace1 workspace2 workspace3; do coder delete "$ws" --yes; done
Deleting a workspace removes:
- Workspace container
/var/lib/dockerDocker named volume (Docker daemon data for that workspace)- Host directory at
/coder-workspaces/<owner>-<workspace>— via destroy-time provisioner in the template
If the provisioner doesn’t run (e.g. Terraform error, or directories orphaned before this feature was added), use scripts/cleanup-deleted-workspaces.sh. See Orphaned Workspace Cleanup below.
Template Updates
Updating Template Configuration
# 1. Edit template.tf
vim user-defined-web/template.tf
# 2. Push updated template
make push-template-user-defined-web
Updating Docker Image
# 1. Edit image/Dockerfile (if needed)
# 2. Increment version (template reads this automatically)
echo "v0.7" > VERSION
# 3. Build and deploy
make deploy-user-defined-web
# Users must rebuild workspaces to get new Docker image
Testing a Template Change Before Activating
Push a new version without making it the default, test it on a single workspace, then promote it when satisfied. This lets you validate changes without affecting other users.
# 1. Push without activating
make push-template-drupal-core ACTIVATE=false
# 2. Find the new version name (top row, status "Unused")
coder templates versions list drupal-core
# 3. Create a test workspace pinned to that version
coder create --template drupal-core --template-version <version-name> test-workspace --yes
# 4. Verify — e.g. for drupal-core, check setup completed correctly
coder ssh test-workspace -- grep -E "Drush|Drupal install" ~/drupal-core/drupal-setup.log
# 5. Promote to active once satisfied
coder templates versions promote drupal-core <version-name>
# 6. Clean up test workspace
coder delete test-workspace --yes
If you push again with ACTIVATE=true (the default) rather than using promote, that also activates the version — the promote step is only needed when you pushed with ACTIVATE=false and want to activate without re-pushing.
Rolling Back
# Revert to previous template version
git checkout <previous-commit> user-defined-web/template.tf
coder templates push --directory user-defined-web user-defined-web --yes
# Users on old version are unaffected
# Users can update to rollback version via: coder update <workspace>
Backup and Maintenance
Workspace Data Backup
Each workspace stores persistent data in:
- Home directory:
/coder-workspaces/<owner>-<workspace>on host - Docker volume: Named volume
coder-<owner>-<workspace>-dind-cache
Backup strategy:
# Backup home directory
tar -czf workspace-backup.tar.gz /coder-workspaces/<owner>-<workspace>
# Backup Docker volume
docker run --rm \
-v coder-<owner>-<workspace>-dind-cache:/source \
-v $(pwd):/backup \
ubuntu:24.04 \
tar -czf /backup/docker-volume-backup.tar.gz -C /source .
# Restore Docker volume
docker volume create coder-<owner>-<workspace>-dind-cache
docker run --rm \
-v coder-<owner>-<workspace>-dind-cache:/target \
-v $(pwd):/backup \
ubuntu:24.04 \
tar -xzf /backup/docker-volume-backup.tar.gz -C /target
Disk Space Cleanup
When disk space is running low, reclaim it in this order:
1. List all workspaces (including other users’)
coder list -a
2. Delete unused workspaces
Stopped or dormant workspaces that are no longer needed can be deleted. This removes the workspace container, Docker volume, and host directory:
# Delete a single workspace
coder delete <owner>/<workspace-name> --yes
# Delete multiple workspaces
for ws in <owner>/<workspace1> <owner>/<workspace2>; do coder delete "$ws" --yes; done
# Delete all workspaces (use with caution)
for ws in $(coder list -a -c workspace | grep -v WORKSPACE); do coder delete "$ws" --yes; done
Check the Coder dashboard for “Last used” times to identify dormant workspaces before deleting.
3. Prune the Docker BuildKit cache
BuildKit caches can accumulate across workspace image builds:
docker buildx prune -f
4. Check remaining disk usage
df -h /data /coder-workspaces
docker system df
Orphaned Workspace Cleanup
When a workspace is deleted, the destroy provisioner automatically removes the host directory at /coder-workspaces/<owner>-<workspace>. Directories can still be orphaned if the provisioner fails or for workspaces deleted before the provisioner was added. Run the cleanup script to reclaim disk space in those cases.
# Dry run — shows what would be deleted without removing anything
./scripts/cleanup-deleted-workspaces.sh
# Actually delete orphaned directories and Docker volumes
./scripts/cleanup-deleted-workspaces.sh --force
Run as your normal user (not root) — the script calls sudo /usr/local/bin/coder-delete-workspace-dir internally for directory removal, and uses docker volume rm (requires docker group membership).
The script:
- Queries
coder list --allto get the current list of active workspaces - Compares against directories in
/coder-workspaces/and Docker volumes namedcoder-*-dind-cache - Reports sizes before deleting
- Refuses to delete anything if the Coder CLI returns no workspaces (guards against misconfigured auth)
Prerequisites: coder CLI authenticated as an admin; user in the docker group; sudo access for directory removal.
Template Versioning
Store template versions in git:
# Tag releases
git tag -a v0.1 -m "Release v0.1"
git push origin v0.1
# Track changes
git log --oneline user-defined-web/template.tf
Monitoring
Check workspace health:
# View workspace logs
coder ssh my-workspace -- journalctl -u coder-agent -f
# Check Docker daemon inside workspace
coder ssh my-workspace -- docker ps
coder ssh my-workspace -- docker info
# Check DDEV status
coder ssh my-workspace -- ddev list
Resource usage:
# Check workspace container resources
docker stats coder-<workspace-id>
# Check disk usage
df -h /coder-workspaces/
docker system df
Troubleshooting
See troubleshooting.md for detailed debugging procedures.
Quick checks:
# Template deployment failed
coder templates list # Check if template exists
terraform validate # Validate template syntax
# Workspace won't start
coder logs my-workspace # View startup logs
docker logs coder-<workspace-id> # View container logs
# Docker daemon issues
coder ssh my-workspace -- cat /tmp/dockerd.log
coder ssh my-workspace -- systemctl status docker
Security Considerations
Sysbox Runtime
Sysbox provides secure nested containers without privileged mode:
- No
--privilegedflag required - Isolated Docker daemon per workspace
- AppArmor and seccomp profiles configured
Security profiles in template.tf:
security_opt = ["apparmor:unconfined", "seccomp:unconfined"]
These are required for Sysbox functionality and are safer than --privileged.
User Isolation
- Each workspace has isolated Docker daemon
- Workspaces cannot access other workspaces’ Docker containers
- Users have sudo access inside their workspace only
Registry Security
- Store registry credentials in Coder secrets or environment variables
- Avoid hardcoding credentials in template.tf
- Use private registries for proprietary images
Network Security
- DDEV uses Coder’s port forwarding (not direct host binding)
- Ports are proxied through Coder server
- Configure firewall rules on Coder server, not individual workspaces
Best Practices
Resource Allocation
- Default: 4 CPU cores, 8GB RAM (suitable for most PHP/Node projects)
- Large projects: 8 cores, 16GB RAM
- Small projects: 2 cores, 4GB RAM
Monitor resource usage and adjust template defaults accordingly.
Template Naming
- Use clear, descriptive names:
user-defined-web,ddev-developer - Version templates for major changes:
user-defined-web-v2 - Avoid generic names:
template1,test
Image Management
- Always tag images with specific versions and
latest - Test images before pushing to production registry
- Document changes in git commit messages
- Keep Dockerfile layer count reasonable (current: ~10 layers)
Workspace Lifecycle
- Short-lived workspaces: Ideal for temporary work, prototyping
- Long-lived workspaces: Personal development environments
- Stop workspaces when not in use to save resources
Documentation
- Keep
CLAUDE.mdupdated for AI-assisted development - Update user docs in
/docs/user/when template changes - Document breaking changes in git tags and release notes
Additional Resources
- Server Setup Guide - Fresh server installation (Docker, Sysbox, Coder)
- User Management Guide - Managing users and permissions
- Troubleshooting Guide - Debugging workspace issues
- Coder Documentation - Official Coder docs
- Sysbox Documentation - Sysbox runtime details
- DDEV Documentation - DDEV usage and configuration