Running DevContainers as a Non-Root User

A devcontainer running as root is both a security risk and a source of root-owned files on your host mount. This page runs it as a non-root user via remoteUser, aligning the UID so workspace files stay yours — least privilege and correct ownership in one move.

Prerequisites

You need a base image with a non-root user (or to create one).

  • A base image providing a non-root user (the official images do).
  • remoteUser set to that user.
  • UID alignment via updateRemoteUserUID where host UIDs differ.

Non-root prerequisitesYou need a non-root user, remoteUser set, UID alignment, and a check.Non-root userin the imageremoteUserselect itUID alignupdateRemoteUserUIDVerifyid -u

Step-by-Step Implementation

  1. Use a base image with a non-root user, and select it.
{
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu@sha256:PINNED",
  "remoteUser": "vscode"
}
  1. Align the UID with the host so bind-mount files stay yours.
{ "updateRemoteUserUID": true }
  1. Create a non-root user if your base lacks one.
RUN useradd -m -s /bin/bash dev
USER dev
  1. Verify the container runs non-root.
devcontainer exec --workspace-folder . -- id -u   # non-zero

Non-root modelA non-root user with aligned UID gives least privilege and correct host ownership.Non-root userleast privilegeremoteUsereditor runs as itUID alignmenthost files stay ownedResultsafe + correct ownership

Common Pitfalls

Root-run issues are an omitted remoteUser or a UID mismatch.

Non-root triageA triage path from a root-running container to non-root with correct ownership.Does the container run as root?YESSet a non-root remoteUserFiles host-owned correctly?YESupdateRemoteUserUID alignedLeast-privilege container

SymptomRoot CauseRemediation
Container runs as rootremoteUser omittedSet a non-root remoteUser
Root-owned files on the hostUID not alignedEnable updateRemoteUserUID
Permission denied writing workspaceContainer UID != host UIDAlign UIDs / fix ownership
Base has no non-root userMinimal imageCreate one in the Dockerfile

Conclusion

Run non-root by default: select a non-root remoteUser and align its UID with the host, so a compromise is contained and workspace files stay owned by you. It is a one-line change with an outsized payoff for both security and ergonomics.

Two payoffsNon-root execution buys least privilege and correct host file ownership.Least privilegeNon-root processContained blast radiusNo accidental root writesCorrect ownershipAligned UIDHost-owned filesWorking Git

FAQ

Why run non-root if it's just my dev machine? Because a devcontainer runs code from a repository, and a compromise (a malicious dependency, a bad script) is far more contained as an unprivileged user. Non-root also prevents root-owned files appearing on your host mount, which break Git and local tooling.

What does updateRemoteUserUID do? It aligns the container user's UID/GID with your host user's, so files the container writes to the bind-mounted workspace are owned by you rather than a mismatched UID. It is the companion to remoteUser for correct ownership.

My base image has no non-root user — now what? Create one in the Dockerfile (useradd + USER) and set remoteUser to it. The official devcontainer base images already provide a non-root vscode user, so prefer those to avoid the extra step.