Development Containers and Remote Environments
A development container describes an environment that tools can create or attach to; a remote environment also changes where code, credentials, files and debugging processes run. Define those boundaries explicitly. Repeatable setup is useful, but it does not automatically make an environment hermetic, secure, or equivalent to production.
Start by mapping the editor, extension processes, application, filesystem, debugger, container engine, and remote host. Then decide which identities and capabilities each needs. The configuration should make common work easier without quietly granting every project the developer's broadest access.
TL;DR
- A dev container describes an environment tools can create or attach to; remote development also moves code, credentials, files, and debuggers to another machine.
- Map where the editor, extensions, application, filesystem, debugger, container engine, and remote host run.
- Grant each part only the identities and capabilities it needs.
- Repeatable setup doesn't make an environment hermetic, secure, or equivalent to production.
- Accept the environment when tests, debugging, mounts, persistence, and the connection path work — not when a shell prompt appears.
Quick Example
This illustrative configuration references a project Dockerfile that is not shown here and assumes the resulting image defines a vscode user. Build and start it in your project to confirm the user, mounts, and forwarded ports.
The configuration is a starting shape, not a runnable complete project. A real Dockerfile must choose and maintain the required base image, tools, dependencies and user. The port entry is not evidence about actual network exposure or authentication. A successful JSON parse validates syntax only; it does not validate the complete schema, engine permissions, debugger or application behavior.
Core Concepts
Environment description and environment evidence differ
VS Code documents devcontainer.json as the configuration used to launch or attach to a development container, with an image, Dockerfile, or Compose setup as a possible starting point. It also describes tool-specific customization and lifecycle commands. Those declarations need verification in the selected implementation. Developing inside a container
Record the base image identity, package/dependency inputs, build arguments, lifecycle commands and important environment settings. A moving image tag or unpinned download can change a rebuild even when the checked-in JSON remains identical. Decide how updates are reviewed rather than confusing a stale pin with a complete maintenance strategy.
Repeatability also depends on mounts and host state. A project that reads a developer's home configuration or relies on an undeclared host binary can still vary across machines. Investigate those dependencies when two developers receive different behavior. A container is a useful boundary to inspect, not a reason to stop asking about inputs.
Permissions follow processes and mounts
Distinguish the user running editor-side tools from the users running the application, build steps and container engine. A non-root application process can still access sensitive mounted files or a powerful host socket. Review the actual capabilities and mount destinations instead of treating a user label as the entire security model.
These checks are a proposed review plan. None of the container, remote-host, debugging or credential behavior was exercised by the JSON parse.
Debugging crosses a real boundary
Know whether the debugger launches the target inside the container, attaches to a remote process, or forwards through another host. Source paths, working directories, generated files and native dependencies must match the actual execution environment. A local breakpoint configuration can look plausible while pointing to a different file than the process loaded.
Test one intentional breakpoint and one handled failure through the real development path. Verify stack traces and source mapping rather than only seeing a debugger attach message. If native debugging requires additional permissions, review the narrow documented requirement; do not add broad capabilities as a default fix for every project.
Secret delivery is separate from ordinary configuration
Keep secrets out of committed image layers, examples and configuration values. Use the approved secret mechanism for the chosen environment, and understand which processes can read the delivered value. An environment variable can be convenient while still being visible to processes, diagnostics or logs within the relevant boundary.
Review repository trust before running lifecycle commands or enabling access to sensitive credentials. Project setup can execute code during creation or attachment. The environment's description is therefore something to review, not just something to open. No repository code or credential flow was executed for this article.
Best Practices
Define a small acceptance path for the environment: create, install dependencies, run tests, debug one scenario, stop, rebuild and reopen. Record the chosen engine and editor integration versions when behavior depends on them. Use an independently prepared machine or approved remote environment to expose missing host assumptions.
Keep development-only conveniences explicit. A forwarded port, debugger permission or broad bind mount may be useful during development and inappropriate for a production image. Maintain a reviewed production build path instead of promoting the development container by implication.
Test file ownership and persistence. A tool that creates files under the wrong identity can leave the workspace difficult to edit after leaving the container. A rebuild that unexpectedly discards generated work can interrupt development even when application code is safe in version control. Document which directories are reproducible and which require preservation.
Plan remote access and retirement with the responsible owner. Identify where repositories and caches reside, how access is removed, and what happens to stored workspace data. A stopped process does not establish that remote data or credentials have been removed under the organization's requirements.
Choose a simpler local setup when container integration adds more operational cost than the project needs. Choose a managed remote environment when access, compute or collaboration requirements justify it and the real controls can be verified. The decision should follow the development workflow and trust boundary, not a claim that one environment shape fits every team.
Common Mistakes
Calling a container hermetic because it has a Dockerfile
Using privileged mode to fix permissions
Committing secrets for easy onboarding
Treating startup as acceptance
FAQ
Is the example immediately runnable?
Not on its own. It references a project Dockerfile and assumes a user that your image must provide, so it shows the configuration's shape. Add your image, application, and permission requirements, then test the full environment.
Does a container replace secret management?
No. Decide how approved credentials reach the required processes, how they can appear in logs or diagnostics, and how access is revoked. Do not bake secrets into a reusable image or commit them to project configuration.
What proves repeatability?
An independently prepared environment can perform the intended build, test and debug workflow from declared inputs. Record the conditions and remaining host dependencies. A successful JSON parse is a much narrower result.