This guide covers daily workflows, tips, and best practices for working with DDEV in Coder workspaces.
- Workspace Lifecycle
- VS Code for Web
- DDEV Workflows
- Git Workflows
- Port Forwarding
- Common Tasks
- Tips and Tricks
- Performance Optimization
Your workspace has two types of storage:
Persistent (survives stop/restart):
/home/coderdirectory (all your files)- Docker volumes (DDEV databases, images, containers)
Non-persistent (reset on restart):
- Running processes
- Temporary files in
/tmp - System packages installed with
apt-get(unless in Docker image)
What this means:
- Your code, databases, and DDEV projects are safe when stopping workspace
- You need to restart DDEV projects after workspace restart
- System-level changes (installed packages) are lost unless in Docker image
# 1. Start workspace (if stopped)
coder start my-workspace
# 2. SSH into workspace (or open VS Code)
coder ssh my-workspace
# 3. Navigate to project
cd ~/projects/my-site
# 4. Start DDEV
ddev start
# 5. Start developing!# 1. Commit your changes
git add .
git commit -m "End of day commit"
git push
# 2. Stop DDEV projects (optional, saves a few seconds on next start)
ddev stop
# 3. Exit workspace
exit
# 4. Stop workspace (saves costs)
coder stop my-workspaceNote: Stopping workspace is optional. Some teams leave workspaces running 24/7.
| State | Description | Billing | Data Preserved |
|---|---|---|---|
| Running | Active, can SSH and access | Yes | Yes |
| Stopped | Powered off, cannot access | No | Yes |
| Starting | Booting up, wait a moment | No | Yes |
| Deleting | Being deleted | No | No |
# List all your workspaces
coder list
# Create second workspace
coder create --template user-defined-web my-second-workspace --yes
# Switch between workspaces
coder ssh my-first-workspace
coder ssh my-second-workspace
# Stop all workspaces
coder stop my-first-workspace my-second-workspaceUse cases for multiple workspaces:
- Different projects with different requirements
- Testing configurations without affecting main workspace
- Separate staging and development environments
- Different resource configurations (small vs large projects)
From Coder dashboard:
- Find your workspace
- Click VS Code under "Apps"
- VS Code opens in new tab
Direct URL (bookmark this):
https://coder.example.com/@me/my-workspace/apps/code
All workspaces come with these extensions automatically installed at workspace start:
| Extension | Purpose |
|---|---|
PHP Debug (xdebug.php-debug) |
Xdebug step debugging |
PHP Intelephense (bmewburn.vscode-intelephense-client) |
PHP IntelliSense, go-to-definition, autocomplete |
ESLint (dbaeumer.vscode-eslint) |
JavaScript/TypeScript linting |
Prettier (esbenp.prettier-vscode) |
Code formatting |
PHPStan (sanderronde.phpstan-vscode) |
PHP static analysis |
Code Spell Checker (streetsidesoftware.code-spell-checker) |
Spell checking in code and comments |
Stylelint (stylelint.vscode-stylelint) |
CSS/SCSS linting |
PHP Sniffer & Beautifier (valeryanm.vscode-phpsab) |
PHPCS/PHPCBF integration |
DDEV Manager (biati.ddev-manager) |
DDEV control panel: start/stop/Xdebug toggle/etc. |
Extensions are installed from the Open VSX Registry — the open-source alternative to the Microsoft Marketplace used by VS Code Server and other non-Microsoft VS Code deployments.
To install an extension manually:
- Open Extensions panel (
Ctrl+Shift+X) - Search for the extension by name
- Click Install
Important: Not all extensions on the Microsoft Marketplace are available on Open VSX. Authors must publish to both registries independently. Some notable extensions that are only on the Microsoft Marketplace (and therefore unavailable here) include Pylance and GitHub Copilot.
If an extension you need is missing, check if it exists on open-vsx.org first. If the author hasn't published there, you can request it via the extension's issue tracker.
Admins can pre-install additional extensions for all users by adding extension IDs to the extensions list in the template's vscode-web module. See the server setup guide for details.
The current setup uses VS Code for Web (browser-based VS Code Server). Other IDE options are possible:
- VS Code Desktop — Connect your locally-installed VS Code to the workspace via the Coder CLI (
coder ssh). Requires the Coder CLI on your machine. - JetBrains IDEs (PhpStorm, GoLand, WebStorm, etc.) — Connect via JetBrains Gateway with the Coder plugin. Full native IDE with remote execution. Currently supported by the Coder platform but not configured in these templates by default.
- Neovim / terminal editors — Available in any SSH session via
coder ssh.
Open terminal:
- Menu: Terminal → New Terminal
- Keyboard: Ctrl+` (backtick)
Multiple terminals:
- Click
+to add terminal - Split terminal with split button
- Switch between terminals with dropdown
Terminal tips:
# Terminal opens in /home/coder by default
# Navigate to project
cd ~/projects/my-site
# Run DDEV commands
ddev start
ddev describe
# Run composer/npm through DDEV
ddev composer install
ddev npm run devOpening projects:
- File → Open Folder
- Navigate to
~/projects/my-site - Click OK
Drag and drop:
- Drag files from your computer into VS Code to upload
- Works for single files and folders (up to reasonable size)
File search:
- Ctrl+P (Cmd+P): Quick file open
- Ctrl+Shift+F (Cmd+Shift+F): Search in files
- Ctrl+Shift+E (Cmd+Shift+E): File explorer
Built-in Git features:
- Source Control panel (Ctrl+Shift+G)
- Stage changes (click
+next to file) - Commit (type message, click ✓)
- Push/pull (click
...menu) - View diff (click modified file)
GitLens extension features:
- Blame annotations (who changed each line)
- Commit history
- Repository explorer
- Compare branches
VS Code supports debugging for most languages:
PHP (with Xdebug via DDEV):
The DDEV Manager extension provides a UI panel for toggling Xdebug. Alternatively, use the terminal:
ddev xdebug on # Enable Xdebug
ddev xdebug off # Disable when done (improves performance)Then in VS Code:
- Add a breakpoint (click left of line number — should appear solid red)
- Press F5 → select "Listen for Xdebug"
- Refresh your browser to trigger the breakpoint
- The bottom bar turns orange when the debug session is active
Node.js:
// .vscode/launch.json
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}/server.js"
}Press F5 to start debugging.
Beyond the pre-installed set, these are useful additions for specific project types (search Open VSX by name):
Drupal:
- Drupal Smart Snippets
WordPress:
- WordPress Snippets
Laravel:
- Laravel Blade Snippets
Node.js:
- npm Intellisense
- ES7+ React/Redux/React-Native snippets
| Action | macOS | Windows/Linux |
|---|---|---|
| Command palette | Cmd+Shift+P | Ctrl+Shift+P |
| Quick open file | Cmd+P | Ctrl+P |
| Terminal | Cmd+` | Ctrl+` |
| Find in files | Cmd+Shift+F | Ctrl+Shift+F |
| Save | Cmd+S | Ctrl+S |
| Format document | Shift+Alt+F | Shift+Alt+F |
| Go to definition | F12 | F12 |
| Find references | Shift+F12 | Shift+F12 |
DDEV supports 20+ project types. Common examples:
WordPress:
mkdir ~/projects/my-wordpress
cd ~/projects/my-wordpress
ddev config --project-type=wordpress --docroot=web
ddev start
ddev composer create roots/bedrockDrupal:
mkdir ~/projects/my-drupal
cd ~/projects/my-drupal
ddev config --project-type=drupal --docroot=web
ddev composer create drupal/recommended-project
ddev drush site:install --account-name=admin --account-pass=admin -yLaravel:
mkdir ~/projects/my-laravel
cd ~/projects/my-laravel
ddev config --project-type=laravel --docroot=public
ddev composer create laravel/laravel
ddev exec php artisan key:generateGeneric PHP:
mkdir ~/projects/my-php-site
cd ~/projects/my-php-site
ddev config --project-type=php --docroot=web
mkdir web
echo "<?php phpinfo();" > web/index.php
ddev startNode.js:
mkdir ~/projects/my-node-app
cd ~/projects/my-node-app
ddev config --project-type=php --docroot=.
npm init -y
npm install express
# Create server.js
ddev start
ddev exec node server.jsImport database:
# From SQL file
ddev import-db --file=dump.sql
# From compressed file
ddev import-db --file=dump.sql.gz
# From URL
ddev import-db --url=https://example.com/dump.sql.gzExport database:
# Export to file
ddev export-db --file=dump.sql.gz
# Export to stdout (for piping)
ddev export-db > dump.sqlDirect MySQL access:
# MySQL CLI
ddev mysql
# Run SQL file
ddev mysql < query.sql
# One-line query
ddev mysql -e "SELECT * FROM users LIMIT 10;"Database GUI (via browser):
# phpMyAdmin (if configured in .ddev/config.yaml)
ddev launch -p
# Or use MySQL Workbench/TablePlus via SSH tunnel:
coder port-forward my-workspace --tcp 3306:3306
# Connect to localhost:3306 from your machineExecute commands in web container:
# Generic command
ddev exec <command>
# Examples
ddev exec pwd
ddev exec ls -la
ddev exec which php
# Run as root
ddev exec sudo <command>SSH into web container:
ddev ssh
# You're now inside the web container
# Run any command
composer install
npm run build
exitComposer commands:
ddev composer install
ddev composer require vendor/package
ddev composer update
ddev composer dump-autoloadnpm/yarn commands:
ddev npm install
ddev npm run dev
ddev npm run build
ddev yarn install
ddev yarn buildCreate custom DDEV commands:
# Create .ddev/commands/web/ directory
mkdir -p .ddev/commands/web
# Create custom command
cat > .ddev/commands/web/mycommand <<'EOF'
#!/bin/bash
## Description: My custom command
## Usage: mycommand [args]
echo "Running my custom command"
# Your commands here
EOF
chmod +x .ddev/commands/web/mycommand
# Use it
ddev mycommandRunning multiple DDEV projects:
# Start all projects
cd ~/projects/project1 && ddev start
cd ~/projects/project2 && ddev start
cd ~/projects/project3 && ddev start
# List running projects
ddev list
# Stop specific project
cd ~/projects/project1
ddev stop
# Stop all projects
ddev stop --all
# Power off all (removes containers)
ddev poweroffPort conflicts: If you run multiple projects simultaneously, they'll use different ports automatically. Check with:
ddev describe
# Shows URLs and ports for each project# Set your identity (one-time setup)
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
# Set default branch name
git config --global init.defaultBranch main
# Useful aliases
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commitYour SSH key is managed by Coder and automatically available in workspaces.
Get your Coder public key:
# Inside workspace
coder publickey
# Copy the output (starts with ssh-ed25519 or ssh-rsa)Add key to your Git host:
- GitHub: https://github.com/settings/keys → New SSH key
- GitLab: https://gitlab.com/-/profile/keys → Add key
- Bitbucket: https://bitbucket.org/account/settings/ssh-keys/ → Add key
Test SSH:
# Test GitHub
ssh -T git@github.com
# Should say: "Hi username! You've successfully authenticated..."
# Test GitLab
ssh -T git@gitlab.com
# Should say: "Welcome to GitLab, @username!"How it works: Coder's GitSSH wrapper uses your Coder-managed SSH key automatically. No need to manage keys inside the workspace.
Clone and start working:
cd ~/projects
git clone git@github.com:username/repo.git
cd repo
ddev config --auto # If .ddev/config.yaml exists
ddev startDaily workflow:
# Pull latest changes
git pull
# Create feature branch
git checkout -b feature/my-feature
# Make changes
# ... edit files ...
# Check status
git status
# Stage changes
git add .
# Commit
git commit -m "Add my feature"
# Push
git push origin feature/my-featureStash changes:
# Stash uncommitted changes
git stash
# Pull latest
git pull
# Restore stashed changes
git stash popMerge conflicts:
# If pull creates conflicts
git pull
# Auto-merging file.php
# CONFLICT (content): Merge conflict in file.php
# Resolve in VS Code (shows conflict markers)
# Edit file, remove <<<<<<, ======, >>>>>>
# Stage resolved files
git add file.php
# Complete merge
git commit# Install pre-commit hooks (if project uses them)
cd ~/projects/my-site
# If using PHP CodeSniffer
ddev composer require --dev phpcodesniffer
# If using ESLint
ddev npm install --save-dev eslint
# Hooks run automatically on git commit
git commit -m "Test commit"
# Runs linters, formatters, etc.In Coder, you don't access services via direct URLs. Instead:
- Service listens on port inside workspace (e.g., DDEV on port 80)
- Coder proxies port through its server
- You access via Coder's secure URL
Traditional DDEV:
https://my-site.ddev.site ← Doesn't work in Coder
Coder DDEV:
https://ddev-web--workspace-name--owner.coder.example.com/ ← Use DDEV Web app URL
Via Coder UI (Recommended):
- Go to workspace in Coder dashboard
- Under Apps, click DDEV Web
- Your DDEV project loads in a new tab
The URL follows the pattern: https://ddev-web--workspace-name--owner.domain/
Common DDEV ports:
- 80: HTTP web server
- 443: HTTPS web server
- 8025: Mailpit web UI (email testing)
- 3306: MySQL (for external DB tools)
# Forward port to local machine
coder port-forward my-workspace --tcp 80:80
# Now access via:
# http://localhost:80
# Forward MySQL for database tools
coder port-forward my-workspace --tcp 3306:3306
# Connect MySQL Workbench or TablePlus to:
# Host: localhost, Port: 3306If you run custom services, add port forwarding in template.tf (ask admin) or use CLI:
# Forward custom port (e.g., Node.js on 3000)
coder port-forward my-workspace --tcp 3000:3000
# Access via:
# http://localhost:3000From Git repository:
cd ~/projects
git clone git@github.com:username/existing-project.git
cd existing-project
ddev config --auto # Auto-detect project type
ddev start
ddev composer install # Or npm install
ddev import-db --file=backup.sql.gzFrom local machine:
# Option 1: Via VS Code drag-and-drop
# Open VS Code, drag project folder into file explorer
# Option 2: Via SCP
coder scp ./local-project my-workspace:~/projects/
# Then in workspace:
cd ~/projects/local-project
ddev config --auto
ddev start# Create snapshot (backup current state)
ddev snapshot
# Restore snapshot (revert to last snapshot)
ddev snapshot restore
# List snapshots
ddev snapshot --list
# Delete snapshots
ddev snapshot --cleanupUse case: Before risky database changes:
ddev snapshot
# Try risky migration
ddev exec drush updatedb
# If fails, restore:
ddev snapshot restoreDownload files from workspace:
# Via SCP
coder scp my-workspace:~/projects/my-site ./local-backup/
# Via VS Code
# Right-click file/folder → DownloadUpload files to workspace:
# Via SCP
coder scp ./local-files my-workspace:~/projects/my-site/
# Via VS Code drag-and-dropBackup entire project:
# Inside workspace
cd ~/projects
tar -czf my-site-backup.tar.gz my-site/
# Download
exit
coder scp my-workspace:~/projects/my-site-backup.tar.gz ./Composer (PHP):
ddev composer install # Install dependencies
ddev composer require pkg/name # Add package
ddev composer update # Update packages
ddev composer dump-autoload # Rebuild autoloadernpm (Node.js):
ddev npm install # Install dependencies
ddev npm install package # Add package
ddev npm update # Update packages
ddev npm run <script> # Run package.json scriptSystem packages (not persistent):
# Install system tool (temporary)
sudo apt-get update
sudo apt-get install <package>
# For permanent install, ask admin to add to Docker imageDDEV logs:
# All containers
ddev logs
# Specific container
ddev logs web
ddev logs db
# Follow logs (like tail -f)
ddev logs -fDocker logs:
# List containers
docker ps
# View logs
docker logs <container-id>
# Follow logs
docker logs -f <container-id>System logs:
# Coder agent logs
journalctl -u coder-agent -f
# Docker daemon logs
journalctl -u docker -f
# Docker daemon logs (written by startup script)
cat /tmp/dockerd.logDebug DDEV:
# Verbose output
ddev start --debug
# Show DDEV config
ddev describe
# Show Docker info
docker info
# Show DDEV version
ddev versionAdd to ~/.bashrc:
# DDEV aliases
alias dl='ddev list'
alias ds='ddev start'
alias dx='ddev stop'
alias dr='ddev restart'
alias de='ddev exec'
# Git aliases
alias gs='git status'
alias gp='git pull'
alias gc='git commit'
alias gco='git checkout'
# Navigation
alias projects='cd ~/projects'
alias ll='ls -la'
# Reload bashrc
source ~/.bashrcPer-project settings (.vscode/settings.json):
{
"php.validate.executablePath": "/usr/bin/php",
"phpcs.executablePath": "/usr/bin/phpcs",
"eslint.workingDirectories": ["web/themes/custom"],
"files.exclude": {
"**/vendor": true,
"**/node_modules": true
}
}Commit this file to Git for team consistency.
Skip DDEV router (for simple projects):
# .ddev/config.yaml
router_disabled: true
# Access via port forwarding instead of ddev URLsDisable unnecessary services:
# .ddev/config.yaml
omit_containers: ["ddev-ssh-agent"]Use shallow Git clones:
git clone --depth 1 git@github.com:user/repo.gitTypical setup:
- Tab 1: VS Code (editing)
- Tab 2: Coder dashboard (port forwarding)
- Tab 3: DDEV project (browser)
- Tab 4: Mailpit (email testing)
Use browser bookmarks for quick access.
Fast workflow:
# Terminal-only workflow (no clicking)
coder ssh my-workspace
cd ~/projects/my-site
ddev start
ddev logs -f & # Background logs
vim web/index.php
git add . && git commit -m "Update" && git push
ddev describe # Get ports, open in browserCheck current resources:
# CPU cores
nproc
# RAM
free -h
# Disk
df -hIf workspace is slow, increase resources:
# Stop workspace
coder stop my-workspace
# Delete and recreate with more resources
coder delete my-workspace --yes
coder create --template user-defined-web my-workspace \
--parameter cpu=8 \
--parameter memory=16 \
--yes
# Restore from Git
cd ~/projects
git clone git@github.com:user/repo.gitDisable Xdebug (when not debugging):
ddev xdebug offUse NFS (if file operations are slow):
# .ddev/config.yaml
nfs_mount_enabled: trueReduce container memory:
# .ddev/config.yaml
resources:
limits:
memory: 2g# Remove unused images/containers
docker system prune -a -f
# Remove all volumes (careful!)
docker volume prune -f
# Check disk usage
docker system dfShallow clones (for large repos):
git clone --depth 1 --single-branch git@github.com:user/repo.gitSparse checkout (for monorepos):
git clone --filter=blob:none --sparse git@github.com:user/repo.git
cd repo
git sparse-checkout set path/to/needed/directory- Getting Started Guide - First-time setup
- Troubleshooting Guide - Debugging
- DDEV Documentation - DDEV reference
- Coder Documentation - Coder platform docs
- GitHub Issues - Report bugs
- DDEV Discord - DDEV community
- Coder Discord - Coder community
| Problem | Quick Fix |
|---|---|
| Docker not working | sudo systemctl restart docker |
| DDEV won't start | ddev poweroff && ddev start |
| Out of disk space | docker system prune -a -f |
| Workspace slow | Stop unused DDEV projects: ddev stop --all |
| Git SSH failing | Re-add SSH key to Coder and Git host |
| Port not accessible | Use Coder UI port links, not direct URLs |
Happy coding! For more help, see Getting Started or Troubleshooting Guide.