This project implements runtime UID/GID remapping to ensure portability across different development environments.
The Docker development environment dynamically adapts to user-specific UID/GID values at container startup, eliminating file ownership conflicts between the container and host filesystem.
The container image uses Ubuntu 24.04's default ubuntu user (UID/GID 1000). Development tools and sudo privileges are configured during image build. No user-specific configuration is required at this stage.
When the container starts:
- The entrypoint script receives host UID/GID values via environment variables
- The
ubuntuuser is remapped to match the host user's UID/GID - Home directory and workspace ownership are updated
- Execution proceeds as the remapped user
Files created within the container maintain correct ownership on the host filesystem. A single container image supports multiple users without rebuilding.
Container Lifecycle:
1. Container starts as root
2. entrypoint.sh receives HOST_UID/HOST_GID
3. ubuntu user remapped (1000 → host UID)
4. Ownership updated on home directory and workspace
5. Execution switches to remapped ubuntu user
6. VS Code connects as ubuntu (with host UID/GID)
- Portability: Single image supports multiple users with different UID/GID values
- No Rebuilds: User changes do not require image reconstruction
- Performance: Minimal overhead during container initialization
- Distribution: Pre-built images can be shared via container registry
- CI/CD Integration: Functions consistently across local, cloud, and pipeline environments
- Conflict Avoidance: Modifies existing user rather than creating new user entries
Primary DevContainer configuration file:
containerUser: "root"- Initial container user (required for UID/GID remapping)remoteUser: "ubuntu"- User context for VS Code connection (post-remapping)containerEnv- Environment variables passing host UID/GID to entrypointinitializeCommand- Pre-startup image build command
Runtime remapping script:
- Reads
HOST_UIDandHOST_GIDfrom environment - Modifies ubuntu user/group to match host values
- Updates file ownership on home directory and workspace
- Executes container command as remapped user
Container image definition:
- Base image: Ubuntu 24.04
- Development toolchain installation
- sudo configuration for ubuntu user
- Entrypoint script integration
- Open project directory in VS Code
- Execute command: "Dev Containers: Reopen in Container"
- VS Code performs the following operations:
- Builds container image if not present
- Starts container with host UID/GID environment variables
- Establishes connection as ubuntu user with remapped credentials
Build the container image:
./scripts/docker/build_image.shStart interactive container session:
./scripts/run.shVerify identity remapping:
docker run --rm \
--env "HOST_UID=$(id -u)" \
--env "HOST_GID=$(id -g)" \
cpp-dev:latest idThe same container image supports different user contexts:
# Developer A (UID 1000)
HOST_UID=1000 HOST_GID=1000 → ubuntu remapped to 1000:1000
# Developer B (UID 1001)
HOST_UID=1001 HOST_GID=1001 → ubuntu remapped to 1001:1001
# CI Environment (UID 5000)
HOST_UID=5000 HOST_GID=5000 → ubuntu remapped to 5000:5000No image rebuilds required for different user contexts.
Execute within container:
idExpected output: uid=<host_uid>(ubuntu) gid=<host_gid>(ubuntu)
Create test file within container:
touch /workspaces/cpp-project-template/test_file
ls -la /workspaces/cpp-project-template/test_fileExpected: File owned by ubuntu user in container
Verify on host:
ls -la test_fileExpected: File owned by host user
docker run --rm \
--env "HOST_UID=5555" \
--env "HOST_GID=5555" \
cpp-dev:latest idExpected output: uid=5555(ubuntu) gid=5555(ubuntu)
Symptom: Files created in container have incorrect ownership on host
Diagnosis: Verify environment variable configuration in .devcontainer/devcontainer.json:
"containerEnv": {
"HOST_UID": "${localEnv:UID}",
"HOST_GID": "${localEnv:GID}"
}Symptom: Container fails to start or VS Code cannot establish connection
Resolution:
- Verify
remoteUser: "ubuntu"in devcontainer.json - Inspect container logs:
docker logs cpp-dev-$(whoami) - Confirm entrypoint script permissions:
chmod +x scripts/entrypoint.sh
Symptom: Write operations fail within container
Resolution:
- Verify entrypoint script execution completed successfully
- Inspect workspace ownership:
ls -la /workspaces - Rebuild container image:
./scripts/docker/build_image.sh
Symptom: Container user displays UID 1000 regardless of host UID
Explanation: If host UID is 1000, no remapping is necessary. The ubuntu user's default UID matches the host, so the remapping operation is a no-op.
GPU support is configured via runArgs:
"runArgs": ["--gpus", "all"]GUI application support is enabled through:
"runArgs": [
"--env", "DISPLAY=${localEnv:DISPLAY}",
"--volume", "/tmp/.X11-unix:/tmp/.X11-unix"
]For debugging operations requiring ptrace, uncomment in devcontainer.json:
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt", "seccomp=unconfined"
]This configuration targets Ubuntu 24.04, which includes a pre-configured ubuntu user. For Ubuntu 22.04 support, user creation must be added to the Dockerfile.
Ubuntu 22.04 does not include an ubuntu user by default. Add the following to the Dockerfile:
FROM ubuntu:22.04
# Create ubuntu user
RUN groupadd --gid 1000 ubuntu \
&& useradd --uid 1000 --gid 1000 -m -s /bin/bash ubuntu \
&& echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/ubuntu \
&& chmod 0440 /etc/sudoers.d/ubuntuThe entrypoint script and devcontainer.json require no modifications.
For a Dockerfile supporting both Ubuntu 22.04 and 24.04:
RUN getent group ubuntu || groupadd --gid 1000 ubuntu \
&& id ubuntu || useradd --uid 1000 --gid 1000 -m -s /bin/bash ubuntu \
&& echo 'ubuntu ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/ubuntu \
&& chmod 0440 /etc/sudoers.d/ubuntuThis approach creates the user on 22.04 and exits gracefully on 24.04 where the user already exists.
ARG USER_ID=1000
RUN useradd -u $USER_ID ...Characteristics:
- Image specific to individual user
- Requires rebuild for different users
- Limited portability across development teams
- Simpler implementation without runtime entrypoint
usermod -u $HOST_UID ubuntuCharacteristics:
- Single image supports multiple users
- No rebuild required for user changes
- Portable across teams and environments
- Additional complexity in entrypoint script
For a multi-user project template, portability and ease of use are prioritized over implementation simplicity. Runtime remapping enables immediate use after repository cloning without build configuration or image rebuilds.
When updating the DevContainer configuration:
- Test with multiple UID values
- Verify file ownership consistency between container and host
- Confirm VS Code connectivity
- Update documentation to reflect behavioral changes