
September 12, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Dev Containers: Reproducible Development Environments solve a problem every team hits eventually. One developer runs PHP 8.3, another still has 8.1, and staging uses 8.4. A Laravel app works locally but fails after deploy because extensions or Node versions differ. I've seen this on production Laravel applications and on legal-tech portals where document uploads depend on specific PHP modules. Dev Containers wrap your project in a defined Docker image and connect your editor to it, so everyone codes inside the same environment. If you already use Docker Compose for local Laravel development, Dev Containers add the missing editor layer that makes onboarding fast.
devcontainer.json file plus Docker to build a shared workspace inside a container. VS Code or compatible editors attach to that container, so PHP, Node, extensions, and tools match on every machine without manual setup.What are Dev Containers and how do they create reproducible development environments?
Dev Containers are a specification maintained at containers.dev. The spec defines how an editor opens a project inside a Docker container instead of on the host OS. Your source code lives in a mounted volume. The container holds the runtime: PHP, Composer, Node.js, database clients, and whatever else the project needs.
The core file is .devcontainer/devcontainer.json. It tells the editor which image to use, which ports to forward, which VS Code extensions to install inside the container, and optional lifecycle scripts. Because that file lives in Git, every clone gets the same definition. Change PHP from 8.3 to 8.4 in one commit, and the whole team picks it up on rebuild.
Reproducibility comes from three layers working together. The Dockerfile or base image pins the OS and packages. Docker Compose can add sidecar services like MySQL or Redis. The devcontainer.json file wires ports, extensions, and post-create commands. Together they replace the README step that says "install PHP, then run these five commands and hope nothing conflicts."
For teams shipping Laravel applications in Nepal or abroad, this matters because hardware varies widely. A developer on Windows with WSL, another on Ubuntu 24, and a contractor on macOS can all open the same container. The app sees identical PHP extensions and identical Composer dependencies.
How do you set up Dev Containers for a Laravel project?
Start with Docker Desktop or Docker Engine on Linux. Install the Dev Containers extension in VS Code. Microsoft documents the workflow at code.visualstudio.com/docs/devcontainers. Create a .devcontainer folder at your project root.
Step 1: Create devcontainer.json
For a Laravel 13 project on PHP 8.3, a practical starting point looks like this:
{
"name": "Laravel Dev",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/var/www/html",
"forwardPorts": [8000, 5173, 3306],
"postCreateCommand": "composer install && cp .env.example .env && php artisan key:generate",
"customizations": {
"vscode": {
"extensions": [
"bmewburn.vscode-intelephense-client",
"onecentlin.laravel-blade",
"dbaeumer.vscode-eslint"
],
"settings": {
"terminal.integrated.defaultProfile.linux": "bash"
}
}
},
"remoteUser": "www-data"
} Step 2: Add Docker Compose for services
Dev Containers work well with a Compose file that defines both the app container and databases. This pattern mirrors what I use in multi-container local development setups:
services:
app:
build:
context: .
dockerfile: .devcontainer/Dockerfile
volumes:
- ..:/var/www/html
depends_on:
- mysql
- redis
mysql:
image: mysql:8.4
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: laravel
volumes:
- mysql-data:/var/lib/mysql
redis:
image: redis:8.10
volumes:
mysql-data: Step 3: Write a focused Dockerfile
Keep the Dockerfile minimal. Install only what Laravel needs:
FROM php:8.3-cli-bookworm
RUN apt-get update && apt-get install -y \
git unzip libzip-dev libpng-dev \
&& docker-php-ext-install pdo_mysql zip gd bcmath \
&& curl -fsSL https://deb.nodesource.com/setup_26.x | bash - \
&& apt-get install -y nodejs \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
WORKDIR /var/www/html Step 4: Reopen in container
Open the project in VS Code. Run the command palette action Dev Containers: Reopen in Container. The first build takes several minutes. Later opens are faster because Docker caches layers. Run php artisan serve --host=0.0.0.0 inside the integrated terminal. Port 8000 forwards to your host browser automatically.
- Commit
.devcontainer/, Dockerfile, and Compose file to Git. - Document any required host env vars in
.env.example. - Add a
postStartCommandif queues or Vite must restart on every open. - Pin image tags instead of using bare
latestfor databases. - Test the flow on a clean machine before onboarding juniors or contractors.
On booking systems like Adventure Third Pole Trek, where Laravel and Livewire share Redis and MySQL, a Dev Container removes the "works on my laptop" friction during feature sprints. New contributors run one command instead of a two-page setup guide.
What is the difference between Dev Containers and Docker Compose alone?
Docker Compose orchestrates containers. Dev Containers add editor integration, automatic extension installation, port forwarding UI, and lifecycle hooks tied to your IDE session. Compose alone still leaves each developer responsible for connecting their editor, installing Intelephense locally, and matching PHP binaries on the host.
| Approach | Reproducibility | Editor integration | Onboarding time | Best for |
|---|---|---|---|---|
| Native host install | Low — drift is constant | Full local speed | Hours to days | Solo devs on one OS |
| Vagrant VMs | High — full VM snapshot | SSH + remote plugins | Moderate — heavy images | Legacy PHP stacks, non-Docker teams |
| Docker Compose only | High for services | Manual — exec into container | Moderate | CI pipelines, headless workflows |
| Dev Containers | High — image + editor config in Git | Native — terminal, debugger, extensions inside container | Low after first build | Teams on mixed OS, Laravel/Node monorepos |
Compose remains the right tool when you only need docker compose up for integration tests in CI. Dev Containers shine when humans need to write code daily inside that same stack. Many projects use both: Compose defines services, and devcontainer.json points at the same file.
Compared to Vagrant, Dev Containers start faster and use less RAM. A full Ubuntu VM for custom software projects can consume 2 GB before your app runs. A PHP Dev Container often sits under 512 MB for the app service alone. The trade-off is that containers share the host kernel, so kernel-level differences between macOS and Linux still require testing in CI.
Which VS Code extensions and tools do you need for Dev Containers?
Install the Dev Containers extension (ID: ms-vscode-remote.remote-containers). On Apple Silicon or Windows, Docker Desktop must be running before you reopen in container. Linux users can use Docker Engine directly, which aligns well with Linux server administration workflows many Nepal teams already follow.
List extensions inside devcontainer.json, not on each developer's host. That way Intelephense, ESLint, and Prettier versions stay aligned. Common picks for Laravel stacks:
- Intelephense — PHP autocompletion using container PHP paths
- Laravel Blade Snippets — template support inside the remote workspace
- ESLint + Prettier — frontend linting for Vite 8.x asset pipelines
- Docker extension — inspect Compose services from the sidebar
- GitLens — blame and history without leaving the remote window
GitHub Codespaces implements the same Dev Containers spec in the cloud. Push your .devcontainer folder, and a teammate opens a browser-based VS Code session with identical tooling. Useful for contractors who refuse to install Docker locally. Costs apply for compute minutes, so local Dev Containers remain cheaper for daily use.
JetBrains IDEs support Dev Containers through remote development gateways. VS Code remains the most documented path. The Docker Compose documentation helps when you debug service networking independent of the editor layer.
How do you troubleshoot common Dev Containers problems?
Most failures fall into a short list. I've hit each of these during production-adjacent development on Laravel upgrades and on database migrations in team environments.
Permission errors on storage and cache
Laravel's storage/ and bootstrap/cache/ directories need write access. If the container runs as root but your host user is UID 1000, files created inside the container may be root-owned on the host. Fix it by matching remoteUser or adding a post-create chown:
"postCreateCommand": "sudo chown -R www-data:www-data storage bootstrap/cache" Slow file sync on macOS and Windows
Bind mounts from Docker Desktop on non-Linux hosts can make vendor/ scans painfully slow. Options include copying dependencies into a named volume, using Mutagen sync, or developing on Linux natively. For large WooCommerce or Magento trees, expect noticeable lag unless you exclude node_modules and vendor from the bind mount.
Port forwarding conflicts
If port 3306 is already used by a local MySQL install, change the host side mapping in Compose:
ports:
- "3307:3306" Update your .env DB_PORT to match. VS Code shows forwarded ports in the Ports panel. You can label them for clarity.
Stale containers after config changes
Editing Dockerfile or Compose without rebuilding leaves you in an old environment. Run Dev Containers: Rebuild Container after changing base images or PHP extensions. This is the Dev Containers equivalent of opcache stale code after deploy — the fix is always a rebuild, not guessing.
For deeper container debugging, read how to debug a running container and rootless containers for security. Scan custom images with guidance from container image scanning with Trivy before sharing internally.
Validate JSON configs with the JSON formatter tool before committing a broken devcontainer.json. A trailing comma there blocks the entire team from opening the project.
How do Dev Containers fit CI, previews, and production parity?
Dev Containers define development parity, not production deployment. Your production server on Ubuntu with Apache and PHP-FPM still needs its own pipeline. The win is that developers stop polluting staging with "only broken on my machine" bugs caused by missing extensions.
Reuse the same Dockerfile in CI for lint and test jobs. GitLab CI can build the Dev Container image, run composer test, and exit. That connects neatly to reproducible builds practices and preview environments for every pull request.
For PostgreSQL-specific apps, swap the MySQL service with patterns from running PostgreSQL in Docker for development. Keep database version pins close to production — MySQL 8.4 LTS in dev when production uses 8.4, not 5.7.
Environment variables belong in .env, never hard-coded in devcontainer.json. Mount secrets through Docker Compose env files listed in .gitignore. This mirrors how Ubuntu environment variables work on bare-metal staging servers.
Teams building REST APIs in Nepal often pair Dev Containers with API-first development workflows. The container runs PHP and Postman collections share the same base URL via forwarded ports. Frontend contractors hit localhost:8000 without installing PHP locally.
Key Takeaways
- Commit
.devcontainer/devcontainer.jsonplus Docker files to Git so every developer gets identical PHP, Node, and extension versions. - Use Docker Compose inside Dev Containers for MySQL, Redis, and other services — do not run databases on the host.
- List VS Code extensions in
customizations.vscode.extensionsso Intelephense and linters match across the team. - Run Rebuild Container after Dockerfile changes; a stale image is the most common hidden version mismatch.
- Fix Laravel permission issues with
remoteUserand targetedpostCreateCommandchown onstorage/. - Reuse the Dev Container Dockerfile in CI pipelines to extend reproducibility from laptop to merge request checks.
People Also Ask
Do Dev Containers work without VS Code?
Yes. The Dev Containers spec is editor-agnostic. VS Code and GitHub Codespaces have the strongest support. JetBrains Gateway and other compatible clients can attach to the same devcontainer.json. CLI-only workflows still use Docker Compose directly without the Dev Containers layer.
Can you use Dev Containers on Apple Silicon Macs?
Docker Desktop for Mac supports ARM images. Use multi-arch base images like php:8.3-cli-bookworm or specify platform: linux/amd64 when you need x86 compatibility for older binaries. First builds may pull larger images; subsequent opens cache quickly.
Are Dev Containers the same as production containers?
No. Dev Containers optimise for developer experience: mounted source, debug extensions, and root access for Composer. Production images should be minimal, non-root, and free of editor tooling. Share the base PHP layer if useful, but keep separate Dockerfiles for dev and prod.
How much RAM do Dev Containers need?
Budget 4 GB for Docker Desktop on macOS or Windows, plus RAM for your app services. A Laravel app with MySQL and Redis typically runs comfortably in 6–8 GB total. Linux hosts with native Docker Engine use less overhead than Desktop VM wrappers.
Ship consistent environments from day one
Dev Containers: Reproducible Development Environments turn onboarding from a half-day install marathon into a single Reopen in Container action. You define PHP 8.3, Composer 2.10, Node.js 26 LTS, and MySQL 8.4 once. Every developer, contractor, and Codespace session inherits that stack from Git. Start with a Laravel or Symfony project, commit the config, and rebuild whenever the stack changes. If your team needs help standardising Docker-based workflows across development and deployment, see web development services or testing and optimization support, then contact us to plan a reproducible setup that matches how you ship to production.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

