DevContainer/Report/25/12/Multi-Env DevContainer Setup
Multi-Environment DevContainer Setup: Patterns and Best Practices
Section 1: Official Guidance (Microsoft & JetBrains) on Multi-Environment DevContainers
- Dev Container Spec & Configuration: The Development Containers (devcontainer) specification is an open standard (now at containers.dev) supported by VS Code, Codespaces, JetBrains IDEs, and others. It does not provide built-in conditional logic or dynamic includes based on environment – each
devcontainer.jsondefines one container configuration. However, you can include multiple devcontainer definitions in a repo (in subfolders) and select one at creation time [1]. For example, GitHub Codespaces supports multiple devcontainer configs and will prompt the user to choose one when creating a codespace [2]. There is no inheritance or automatic merging of settings between multiple configs – they are distinct parallel definitions [2]. - Default Mount Behavior: By default, VS Code's devcontainers bind-mount the workspace folder from the host into the container (for local and Codespaces usage) [3]. This is convenient, but on certain platforms (e.g. Docker Desktop on Mac/Windows) it relies on file-sharing mechanisms (osxfs, gRPC FUSE) that can be slow or memory-intensive. Microsoft's documentation notes you can override this via the
workspaceMountproperty to use a named volume instead of a bind mount [4]. In fact, Microsoft explicitly suggests using a named volume for the entire source tree when working with remote Docker hosts or to improve performance [4]. This technique seeds the container with a fresh clone of the repo inside a Docker volume rather than mounting from the host. - JetBrains Gateway Behavior: JetBrains' implementation of devcontainers (as of 2024+) is still maturing and follows the devcontainer spec but with some differences in source handling. JetBrains IDEs (Gateway or IntelliJ with the Dev Containers plugin) typically clone the repository into a Docker volume by default for remote dev containers [5]. Their system creates a volume (e.g.
jb_devcontainer_sources_xxx) and populates it with the source, rather than bind-mounting your local workspace [5]. This approach avoids the gRPC FUSE file-sharing layer and its issues. In practice, JetBrains prefers using a named volume for the project code when creating a container from a VCS URL, precisely to dodge the performance and file-sync problems that can occur with bind mounts over remote connections (the "gRPC FUSE memory issues" the user noted). For local projects opened via Gateway, JetBrains can also work with a bind mount, but the remote usage is their primary focus. - No Native Conditional Config: Neither VS Code's devcontainer tooling nor JetBrains' currently provide an official way to have one devcontainer config automatically adjust mounts per environment. The devcontainer.json schema doesn't support conditional sections (no built-in
iffor "if running in Codespaces use X, else Y"). The expectation from the spec is that if you have different needs, you either make the configuration handle both (e.g. via environment variables or runtime detection) or supply separate configs. Microsoft's documentation suggests using multiple devcontainer.json files for different scenarios (like separate subfolders for different services or environments) [6]. JetBrains' documentation similarly allows you to point the IDE at a specific devcontainer.json path if it's not in the default location [7] [8] – meaning you could maintain a JetBrains-specific devcontainer config in a separate folder and choose it when creating the Dev Container in Gateway. - Mount and Volume Best Practices: The officially recommended approach is to use bind mounts for local and Codespaces (where the project is already cloned on disk) and to use named volumes for remote scenarios where performance is a concern. Microsoft's "Advanced Container Configuration" guides explicitly mention using
workspaceMountto switch to a named volume for better disk I/O on remote Docker hosts [4]. JetBrains, by automatically cloning to a volume, implicitly follows this best practice for Gateway scenarios. In short: a bind mount of the source is simplest and works well when the host and container are the same machine (VS Code locally, Codespaces VM), while a named volume (with the source copied in) is preferable when the host-file sharing is problematic (JetBrains remote). The challenge is that a single devcontainer.json can't easily do both at the same time without manual steps.
- Dev Container Spec & Configuration: The Development Containers (devcontainer) specification is an open standard (now at containers.dev) supported by VS Code, Codespaces, JetBrains IDEs, and others. It does not provide built-in conditional logic or dynamic includes based on environment – each
Section 2: Real-World Examples of Multi-IDE DevContainer Setups
- Many projects have recognized the need to support different devcontainer settings for different IDEs or cloud environments. Here are three approaches seen in the wild:
- Example 1 – The Red Guild's Multi-Config Repository: The Red Guild's devcontainer repository offers multiple
.devcontainer/…directories, each tailored to a scenario. Notably, it includes a.devcontainer/codespaces/configuration intended specifically for GitHub Codespaces [9]. Their project structure shows parallel configs for "airgapped", "hardened", "minimal" etc., and a separate "codespaces" variant that omits certain run arguments that might not work in Codespaces [9] [10]. This pattern allows them to maintain a base Dockerfile and vary only what's needed per environment. A developer launching VS Code locally might use one config (with host bind-mount), whereas Codespaces users can explicitly choose the codespaces config (which presumably uses the bind mount of the pre-cloned repository but adjusts other settings), and another environment could be chosen for high-security needs. The ReadMe instructs users to pick the appropriate devcontainer folder and "Reopen in Container," leveraging VS Code's ability to list multiple devcontainer.json options [10]. - Example 2 – Mastodon (Open Source Social Network): Mastodon's repository introduced a dedicated devcontainer setup for Codespaces alongside the default devcontainer. In their repo, you'll find
.devcontainer/devcontainer.json(for general use) and a separate.devcontainer/codespaces/devcontainer.jsonwith tweaks for Codespaces. The maintainers noted in commit logs that they updated "Docker/devcontainers/codespace" configs to make Mastodon easier to run in Codespaces [11]. Likely, the Codespaces config removes or modifies mounts and services that don't work in the cloud environment. This mirrors what we saw in example 1: a second devcontainer definition optimized for a different environment. (JetBrains users could conceivably add another, e.g..devcontainer/jetbrains/devcontainer.json, if needed for IDE-specific settings.) - Example 3 – Discourse and Others: The Discourse project (popular forum software) similarly merged changes to support both local devcontainers and Codespaces. They maintain a single
.devcontainerfolder but include logic and documentation for using it on Codespaces (and have blogged about making the devcontainer work smoothly on Codespaces). In their case, they use a Compose override file strategy: adocker-compose.codespaces.ymlthat adjusts volumes and other settings when running in Codespaces. Developers can specify this when launching a Codespace. This pattern – using alternate Compose files or override files – is another real-world solution, though as we discuss in Section 3, it has caveats. - Aside from specific repositories, community templates and starter kits have begun including multi-environment support. For instance, official Dev Container Templates (like those in the
devcontainers/templatesrepo) often include notes for Codespaces. JetBrains has also published an examples repo (JetBrains/devcontainers-examples) demonstrating various features (mounts, volume usage, etc.) in JetBrains IDEs [12], which can serve as a reference for how to configure volumes and mounts in a JetBrains-compatible way. The key takeaway from real projects: they often maintain separate config files or Compose variants for different targets, rather than one config that magically knows about VS Code vs JetBrains vs Codespaces.
Section 3: Docker Compose Override Behavior Across Tools
- Using Docker Compose with devcontainers introduces another layer of complexity when trying to support multiple environments. A common idea is to use
docker-compose.override.yml(which Docker Compose will automatically apply if present) to override the workspace volume mount depending on environment. In practice, the behavior differs by tool: - VS Code (Local): The VS Code Dev Containers extension can respect a Compose override file. If you do not explicitly specify compose files in
devcontainer.json(or if you intentionally include an empty list fordockerComposeFile), the tooling falls back to the default Compose behavior, which does mergedocker-compose.ymlwithdocker-compose.override.ymlif the latter exists [13] [14]. This means a local setup can keep an override file (e.g. git-ignored, with machine-specific volume settings) and VS Code will automatically use it duringdocker-compose up. In fact, one workaround some have used is setting"dockerComposeFile": []in devcontainer.json to let Compose auto-detect files – this trick works in VS Code's implementation, allowing the standarddocker-compose.override.ymlto apply [13]. - GitHub Codespaces: In Codespaces, the devcontainer configuration must list the Compose files explicitly. Historically, Codespaces did not allow an empty
dockerComposeFilearray (it resulted in a build error) [13]. If you listed onlydocker-compose.yml, the platform would not automatically include an override file unless you manually added it to the config. Essentially, Codespaces runs a non-interactive build using the files you specify in devcontainer.json – it doesn't implicitly look fordocker-compose.override.yml. The GitHub Community discussion confirms that an empty array hack wasn't accepted by Codespaces in 2022 [13]. As a result, to use an override in Codespaces, you'd have to commit it (since Codespaces builds from the repo content) and reference it explicitly indevcontainer.json's"dockerComposeFile"list. That negates the idea of keeping it local-only. In practice, many avoid override files in Codespaces configs and instead create separate Compose yaml files or devcontainer variants for different scenarios. - JetBrains Gateway: JetBrains' devcontainer support for Docker Compose is still catching up. As of early 2025, there have been bug reports that certain Docker Compose properties and files aren't handled as expected by JetBrains (e.g. issues where Compose-based devcontainers fail to build or can't find the right context) [15]. If you have a
docker-compose.override.ymlthat's not checked into Git, JetBrains Gateway will not know about it – the IDE will typically clone your repository and then invoke Docker (via the local CLI or an SSH Docker context) to build/run the devcontainer. Since the override file is .gitignored, it won't be present on the remote side, and thus won't be applied. Moreover, JetBrains currently doesn't provide a UI to specify additional compose files on rebuild; it will use the files referenced in devcontainer.json (or auto-detected in the project). In the user's case, this meant the override worked locally (VS Code found the file on disk), but JetBrains ignored it, sticking with the base configuration. This is a known pain point – JetBrains' issue tracker has notes about difficulties supporting "multiple different configurations at once" for devcontainers (like switching between bind and volume mounts) in their current implementation. - Bottom line: Relying on a gitignored compose override is not a portable solution. VS Code's devcontainer extension might honor it locally, but Codespaces will likely skip it, and JetBrains will never see it. A committed override file could be used (and explicitly referenced per environment), but that reintroduces complexity in the devcontainer.json. It may be clearer to use separate compose files or devcontainer definitions rather than one override that some tools inadvertently ignore. If you do use Compose override files, ensure they are checked in and referenced explicitly. Otherwise, expect inconsistent behavior: e.g., JetBrains Gateway on rebuild will just use the primary compose file, which can lead to the wrong volume mount type (and thus the Gateway "gRPC FUSE" issue reappears).
- Using Docker Compose with devcontainers introduces another layer of complexity when trying to support multiple environments. A common idea is to use
Section 4: Alternative Approaches and Trade-Offs for Multi-Environment Config
- Given the limitations above, teams have explored several approaches to support VS Code, JetBrains, and Codespaces in parallel:
- A. Multiple DevContainer Configs: As seen in Section 2, maintaining separate
.devcontainerdirectories for each environment is a straightforward (if duplicated) solution. For example, you might have a base.devcontainer/devcontainer.json(optimized for VS Code and Codespaces using bind mounts) and another at.devcontainer/jetbrains/devcontainer.json(which configures the workspace as a named volume and perhaps includes the SSH server feature needed for Gateway). Developers would then choose the appropriate one. Pros: Clear isolation of settings; each environment gets a config tailored to its needs (no weird hacks). They can diverge on other things too (e.g. JetBrains config might omit VS Code-specific settings and include JetBrains plugin setups instead). Cons: Duplication of a lot of config content – changes have to be propagated to both files. It's mitigated somewhat if you keep most logic in the Dockerfile or Compose and just vary the mount and a few settings. Documentation is needed so users know which config to use (though Codespaces UI and JetBrains's Gateway prompt can list them by name [10]). This approach is currently the most reliable and is officially supported by the spec (which anticipates multiple configs for different scenarios) [1]. - B. Single Config with Conditional Logic Hacks: Some advanced users attempt to use one devcontainer.json and handle differences via environment variables or entrypoint scripts. For example, one could set an environment variable in Codespaces (Codespaces sets
CODESPACES=truein the env by default) and have the Dockerfile or a startup script detect that to adjust behavior. You might copy the repo into a volume at container startup if running under JetBrains, for instance. However, you cannot change the Docker mount type at runtime – it must be decided at container launch. So these hacks only go so far. One could do something like: in devcontainer.json, set"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind"by default, but allow an override via an env var like${localEnv:DEVCONT_MOUNT}. Then instruct JetBrains users to set that env to a named volume mount string. This is pretty unwieldy and not officially documented. Pros: Single source of truth in theory. Cons: Highly error-prone, not well-supported by tooling, and requires user intervention or custom launch commands (which Gateway users can't easily do via UI). In practice, this is not a popular approach due to its fragility. - C. Compose Profiles or Separate Compose Files: Docker Compose has a "profiles" feature and of course supports multiple
-ffiles. You could create, say, adocker-compose.jetbrains.ymlthat redefines the workspace volume as a named volume, and adocker-compose.codespaces.ymlfor Codespaces-specific tweaks. Then you'd run Compose with the appropriate files. VS Code's devcontainer.json can list multiple compose files in an array – but it's static per config. You could decide to commit all these files and have one devcontainer.json that always includes both the base and (for example) the JetBrains override. A JetBrains user could then be told to simply rebuild (ensuring the override file is in the repo). However, including a JetBrains-specific volume override in the default config could break Codespaces (e.g. if that override tries to mount a volume in a way Codespaces doesn't allow). Alternatively, you maintain different branches or a script to swap out compose files. This quickly becomes complex. Pros: Leverages Compose's native override mechanism properly if done right. Cons: Hard to integrate with the devcontainer tooling UI, and not much better than multiple configs. In fact, it's effectively the same as multiple devcontainer definitions, just splitting logic between files. - D. External Launch Scripts / Tools: Some teams sidestep these limitations by using wrapper scripts or alternative tools. For instance, a
devcontainer.jsoncan have a"postCreateCommand"that runs a script; theoretically, that script could detect the IDE or environment and adjust mounts (though again, mounts can't be changed once the container is up – at most you could bind mount an additional volume for node_modules or similar). This doesn't solve the core workspace issue. Another angle is using a tool like DevPod or Coder to manage dev environments. These tools can read devcontainer configs but also allow additional flags or settings per environment. For example, DevPod (formerly Codespaces CLI) can be used to launch a devcontainer on your own VM and you could pass a different mount type. However, this shifts the workflow away from the simple VS Code/Codespaces/Gateway UI buttons, which the question specifically wants to avoid (JetBrains users shouldn't have to run custom CLI commands). - E. Always Use Named Volume + Sync Scripts: A radical approach is to standardize on the "lowest common denominator" – i.e., use a named volume for the workspace in the devcontainer config for everyone. Then handle the syncing of code into/out of that volume. VS Code doesn't do this by default (it wants to mount your folder), but there is a command "Clone Repository in Named Container Volume" which essentially creates a devcontainer with a volume and checks out the repo inside it. If you set
"workspaceMount": "source=my_dev_vol,target=/workspace,type=volume"in devcontainer.json, VS Code will launch an empty volume. You'd need a mechanism (perhaps a"initializeCommand") to copy your code into the container on first run. Codespaces similarly would start with an empty volume (since Codespaces normally expects the code to be in/workspacesvia bind mount). You could add a script that detects if running in Codespaces (perhaps via an env var or the presence of/workspacesmount) and moves the code into the volume, then re-mounts it – but this is quite complex. JetBrains would be happy (it's doing essentially that already), but VS Code and Codespaces workflows would become non-standard. Pros: One config, theoretically consistent environment (all in a volume). Cons: Complex setup and potential for code not syncing back to host (for local VSCode, if you edit in container, you'd have to manually copy files back out if not bind-mounted!). This is generally not recommended unless you truly want an isolated ephemeral workspace for all users. - Given these alternatives, the most maintainable approach is (A) separate devcontainer configs or compose files, explicitly chosen per environment. This avoids guessing logic and lets each IDE do its thing in a supported way. The cost is keeping them in sync, but that can be managed by factoring common parts (like having both reference the same Dockerfile, and only differ in the
workspaceMount/workspaceFolderand perhapsmountsarray). - Secrets Management Considerations: Each environment may handle secrets differently, so your multi-env setup should account for that:
- Codespaces: The recommended method is to use GitHub Codespaces secrets, set via the developer's GitHub account or organization, which get injected as environment variables into the devcontainer. As noted in a Codespaces guide, using local
.envfiles is not feasible on a cloud instance; instead, you create secrets in the Codespaces settings and they'll be available in the container environment [16]. This is secure and avoids committing sensitive info [17]. - VS Code (Local): Developers can use a
.envfile (listed indevcontainer.jsonvia theenvFileproperty) or useremoteEnv/containerEnvto map local environment variables into the container. For example, you might have"remoteEnv": { "API_KEY": "${localEnv:API_KEY}" }to pull a secret from the developer's machine into the container. This requires each developer to set that env var locally. It's convenient but not centrally managed. Alternatively, some use tools like VS Code's Secrets Store (if using Remote - Containers extension) – but that's relatively simple (just avoids putting the secret in logs, still user-provided). - JetBrains Gateway: JetBrains does not yet have an equivalent to Codespaces secrets. If you launch a devcontainer via Gateway, any env vars defined in devcontainer.json
containerEnvwill be set. There isn't a UI to supply secret values at launch (apart from perhaps using environment variables on the SSH host). The pragmatic solution is similar to local: use an.envfile or prompt. One could use the devcontainerfeaturesthat prompt for user input (the devcontainers spec supports a userEnvProbe to ask for input on first attach, which could be used for secrets). But this may or may not be fully implemented in JetBrains' plugin. In practice, Gateway users might need to ensure their secrets are available via some config or manually edit the devcontainer config to add them. This is an area still catching up – so in your docs, explicitly mention how to handle secrets for JetBrains (maybe instruct them to use environment variables on their remote host or add an.envbefore building).
- Codespaces: The recommended method is to use GitHub Codespaces secrets, set via the developer's GitHub account or organization, which get injected as environment variables into the devcontainer. As noted in a Codespaces guide, using local
- In summary, each approach has trade-offs in maintenance vs. automation. The trend in the community has been toward either multiple lightweight configs or a single config that's conservative enough to work in all environments (the latter usually meaning "don't do anything Codespaces/JetBrains can't handle"). For example, avoiding bind mounts of host paths that won't exist in Codespaces, etc., and using the lowest common features.
Section 5: Recommended Strategy (with Ranking of Solutions)
- Considering reliability, maintainability, and user experience – especially that JetBrains Gateway users should be able to rebuild via the IDE GUI without extra steps – here's a recommended approach:
- 1. Provide Dedicated Configs for Different Environments (Preferred): Create one devcontainer configuration for the VS Code & Codespaces case (since their needs are similar: workspace mounted from an existing clone), and one for the JetBrains case (workspace as a volume). For example, keep your current
.devcontainer/devcontainer.jsonfor VS Code/Codespaces, and add.devcontainer/jetbrains/devcontainer.jsonfor Gateway. In the JetBrains config, you would set"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=volume"or simply omit it and let JetBrains clone into itsjb_devcontainer_sourcesvolume (JetBrains might do this automatically if no bind mount is specified) [5]. You should also include the SSH server feature in the JetBrains config (e.g. add the feature"ghcr.io/devcontainers/features/sshd:1": { "version": "latest" }) because JetBrains Gateway uses SSH to connect into the container [18] [19]. Meanwhile, the VS Code/Codespaces config can use a bind mount for/workspace(which VS Code does by default if you don't override it) and include VS Code-specific settings/extensions. Maintenance impact: minimal duplication if you reference the sameDockerfileand share dev environment setup steps. You'd document that VS Code and Codespaces will auto-detect the base config, and JetBrains users should select the "jetbrains" config path when prompted in Gateway [7] [8]. This approach ranks highest in reliability – each tool gets a tested, working setup – at the cost of a tiny bit of onboarding instruction. - 2. Use Compose Overrides or Conditional Includes (Use with Caution): If maintaining two configs feels too heavy, an alternative is to commit a JetBrains-specific Compose file (or override) and instruct JetBrains Gateway to use it. For example, you could check in
docker-compose.jetbrains.ymlthat redefines the volume, and mention in your README that JetBrains users should run a one-time setup to copy that file asdocker-compose.override.ymlon the remote host before rebuild, or use a custom devcontainer path that includes it. However, this is less ideal: it's not automated and Gateway doesn't make it trivial. All tools do support standard Docker behavior when properly configured – e.g., if both VS Code and JetBrains see an override file in the project, they should both apply it (VS Code does by default; JetBrains, if the file is there, should treat it as part of Compose context). The issue is ensuring the override file is present in all environments (Codespaces & Gateway remote). If you went this route, you'd likely commit the override to source control (so it's not secret) and explicitly list it indevcontainer.json:dockerComposeFilefor clarity. This way, JetBrains will use it on rebuild, and Codespaces will include it. Drawback: Every developer, even VS Code ones, now have this complexity, and if someone forgets to update both compose files, things break. So this ranks below separate configs in maintainability. - 3. Single Config with Named Volume for All (Not Recommended in your scenario): Using a named volume for the workspace universally would solve the JetBrains FUSE issue and work in Codespaces, but it breaks the simplicity of local dev (your local VS Code wouldn't have your files on the host synced automatically). It also complicates Codespaces: the Codespace would start with an empty volume, requiring a clone of the repo inside the container each time (losing the benefit of the pre-cloned repo). Users would have to manually export/import code if they want to edit outside the container. This is essentially what JetBrains does under the hood, but applying it to VS Code users would be a regression in UX. Thus, we don't recommend this except in very special cases (e.g., a throwaway environment where the code is truly ephemeral).
- Final Recommendation: Adopt the multi-config approach, as it offers clarity and robustness. Keep your main devcontainer for VS Code and Codespaces with the standard bind mount (workspace folder). Create a parallel devcontainer for JetBrains that uses a volume (or let JetBrains default to doing so). Ensure the JetBrains one includes anything needed for Gateway (SSH server feature, perhaps increase memory if needed, etc.). This way, JetBrains Gateway users can simply open the project via Gateway, and when prompted to pick a devcontainer config, choose the JetBrains-specific one – the IDE will handle the rest, building with the correct volume mount and connecting via SSH seamlessly. They will not need to run any manual Docker commands or special CLI options, which aligns with your requirement.
- Maintainability: To ease maintenance, factor common setup into the Dockerfile or a common compose file that both configs use. For example, both devcontainer.json files can point to the same
docker-compose.ymlfor defining services, but the JetBrains one might supply an override to tweak the mount. Or both use the same Dockerfile for the main container image build. Thus, updates to dependencies or tools only happen in one place. The differences in the configs will be just theworkspaceMount/workspaceFolderand maybefeaturessection. - Secrets: Document a clear strategy for secrets for each environment. For instance, "Put secrets in
.env(which is listed in .gitignore) and list that in devcontainer.json for local development." In the Codespaces config, do not use.env– instead, instruct users to set Codespaces secrets via the GitHub UI (e.g., a secret calledMY_API_KEYthat will be injected – and ensuredevcontainer.jsondoesn't hardcode any secret values) [16]. In the JetBrains config, you might also reference an.envor encourage users to set environment variables on the remote machine before creating the container. Since JetBrains uses the local Docker CLI to initiate remote builds [20], it might carry over your local env if configured, but this is not guaranteed. Simpler is telling JetBrains users to add needed secrets to the devcontainer config'scontainerEnv(JetBrains will respect that on startup). For example, in the JetBrains devcontainer.json:"containerEnv": { "MY_API_KEY": "${env:MY_API_KEY}" }– if the user has that env var set on their Gateway launch environment (or you prompt for it with a feature), it will populate. - In conclusion, ranking solutions by reliability: (1) separate configs (most reliable, no cross-conditional complexity) > (2) committed override compose files (works if disciplined, but easy to get wrong) > (3) one-size-fits-all config with hacks (least reliable). The separate config approach is used in multiple production projects successfully, and it aligns with official guidance that multiple devcontainer definitions are supported [1]. By implementing that and clearly guiding users of each IDE on how to select the right configuration, you'll achieve a seamless DevContainer experience across VS Code, JetBrains Gateway, and Codespaces.
Footnotes
[3] https://code.visualstudio.com/remote/advancedcontainers/change-default-source-mount
[4] https://code.visualstudio.com/remote/advancedcontainers/change-default-source-mount
[5] https://www.jetbrains.com/help/idea/faq-about-dev-containers.html
[7] https://blog.jetbrains.com/idea/2024/07/using-dev-containers-in-jetbrains-ides-part-1/
[8] https://blog.jetbrains.com/idea/2024/07/using-dev-containers-in-jetbrains-ides-part-1/
[12] https://www.jetbrains.com/help/idea/dev-container-limitations.html
[15] https://www.reddit.com/r/Jetbrains/comments/1anq4ks/whats_going_on_with_devcontainers/
[19] https://blog.kinto-technologies.com/posts/devcontainer_codespaces_en/
[20] https://www.jetbrains.com/help/idea/faq-about-dev-containers.html