Only initializeCommand runs on the host machine. The others, onCreateCommand, updateContentCommand, postCreateCommand, postStartCommand and postAttachCommand, run inside the container. When you open a repository that has a dev container, that first command can execute on your own machine before the container exists.
The commands in order
The dev container specification lists command properties that run at different points in the container's lifecycle. Here is the picture drawn from the reference:
| Command | Runs where | When |
|---|---|---|
| initializeCommand | On the host | During initialization, at creation and on later starts, possibly more than once |
| onCreateCommand | In the container | First of three setup commands, right after first start |
| updateContentCommand | In the container | Second of three |
| postCreateCommand | In the container | Last of three, after the container is assigned to a user |
| postStartCommand | In the container | Each time the container starts |
| postAttachCommand | In the container | Each time a tool attaches |
The reference notes that initializeCommand runs wherever the source code is located. For cloud services, that is in the cloud, not on a laptop.
Why the host command matters
Everything else is at least in the container, subject to its mounts and settings. The first command is not. It runs where the repository is checked out, with your account's access to local files. Read it whenever you open a repository you did not write.
A real review pass takes seconds. Search the dev container file for initializeCommand, read what it calls, and be suspicious of downloads piped to a shell or scripts you cannot see in the diff. See the open an unfamiliar repo checklist for the rest of the routine.
The three setup commands and secrets
The reference explains that onCreateCommand is typically run for caching or prebuilding in cloud services, so it will not usually have user scoped assets or secrets. postCreateCommand runs once the container is assigned to a user, which lets cloud services use user specific secrets and permissions at that point. A script in postCreateCommand can therefore see more than one in onCreateCommand, in some hosted setups.
Format details with a safety angle
Each command can be a string, an array or an object. The reference says an array is passed to the operating system without a shell, a string goes through a shell, and an object runs several commands in parallel. A string lets a command use shell features like pipes, which is flexible and also easier to hide things in. The array form is easier to read in review.
The waitFor setting controls which command a tool waits for before connecting, and defaults to updateContentCommand. Steps in postCreateCommand can therefore keep running after you connect.
A review checklist
- Find every lifecycle command in the file, including ones added by Features.
- Flag any initializeCommand and read the script it calls.
- Flag network downloads, shell pipes and commands that write outside the workspace.
- Prefer array form so the exact program is visible.
- Ask whether the work belongs in the image build instead, where it is pinned and scanned.
For the broader settings review, see what a code reviewer should flag in devcontainer.json.
Safeguard scans the dependencies a setup command installs, and you can try the free scan at gold.safeguard.sh/scan.
Quick check
Does initializeCommand run inside the container?
No. The specification says it runs on the host machine, wherever the source code is located.
Is the array form of a command safer than the string form?
It avoids a shell, so what runs is easier to see. It does not by itself make a command safe.
Sources
Last reviewed: 19 October 2026.