Introduction
Docker has become a popular platform for developers to create, deploy, and run applications in a portable, consistent environment.
By default, Docker containers run as the root user, which can pose security risks if the container becomes compromised.Β
Also, running as root can be an issue when sharing folders between the host and the docker container.
To reduce these risks, we'll discuss running a Docker container with a custom non-root user that matches your host Linux user's user ID (UID
) and group ID (GID
), ensuring seamless permission handling for mounted folders.
Running a docker build command that uses (mainly) a non-root user might force us to use sudo
for some commands.
The same is valid for running the docker itself using unattended scripts.
You may need elevated privileges for specific tasks.
Granting password-less sudo permissions to a non-root user allows you to perform administrative tasks without the risk of running the entire container as the root user.
Here are the steps to create and run a Docker container with a non-root user and password-less sudo permissions:
Step 1: Adjust the Dockerfile to Accept UID and GID as Arguments
Modify your Dockerfile to accept the host's UID and GID as arguments. This way, you can create a user in the container with a matching UID and GID.
Add the following lines to your Dockerfile:
FROM ubuntu
ARG UID
ARG GID
# Update the package list, install sudo, create a non-root user, and grant password-less sudo permissions
RUN apt update && \
apt install -y sudo && \
addgroup --gid $GID nonroot && \
adduser --uid $UID --gid $GID --disabled-password --gecos "" nonroot && \
echo 'nonroot ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers
# Set the non-root user as the default user
USER nonroot
Step 2: Set the Working Directory
Set the working directory where the non-root user can access it. Add the following line to your Dockerfile:
# Set the working directory
WORKDIR /home/nonroot/app
This sets the working directory to '/home/nonroot/app', where the non-root user has read and write permissions.
Step 3: Copy Files and Set Permissions
Ensure the non-root user has the necessary permissions to access the copied files. Add the following lines to your Dockerfile:
# Copy files into the container and set the appropriate permissions
COPY --chown=nonroot:nonroot . /home/nonroot/app
RUN chmod -R 755 /home/nonroot/app
Step 4: Build and Run the Docker Container with UID and GID Parameters
Now you can build the Docker image and run the container with the custom non-root user. Pass your host's UID and GID as build arguments to create a user with matching permissions.
Use the following commands to build and run your container:
# Get your host's UID and GID
export HOST_UID=$(id -u)
export HOST_GID=$(id -g)
# Build the Docker image
docker build --build-arg UID=$HOST_UID --build-arg GID=$HOST_GID -t your-image-name .
# Run the Docker container
docker run -it --rm --name your-container-name your-image-name id
The docker output will be:
uid=1000(nonroot) gid=1000(nonroot) groups=1000(nonroot)
Optional - Adding Docker Compose for Running a Custom Non-Root User Container
Docker Compose is a tool for defining and running multi-container applications using a YAML file to configure the application's services, networks, and volumes.
It simplifies managing containers, especially when working with multiple services.
This section will discuss how to use Docker Compose to run a Docker container with a custom non-root user that matches your host's UID and GID.
Create a docker-compose.yml
file in your project directory with the following content:
version: '3.8'
services:
your_service_name:
build:
context: .
args:
UID: ${HOST_UID}
GID: ${HOST_GID}
image: your-image-name
container_name: your-container-name
volumes:
- ./app:/home/nonroot/app
This YAML file defines a service, your_service_name
, using the Dockerfile in the current directory. The build
section passes the UID
and GID
build arguments from the host environment variables HOST_UID
and HOST_GID
. The volumes
section maps a local directory (./app
) to the container's working directory (/home/nonroot/app
), ensuring seamless permission handling for the mounted folder.
First, to run the container using Docker Compose set the HOST_UID
and HOST_GID
environment variables in your host system.
The following command will build the docker (if needed), start it, print the user ID, and remove the container:
HOST_UID=$(id -u) HOST_GID=$(id -g) docker compose run --rm your_service_name id
Conclusion
Running a Docker container with a custom non-root user that matches your host's UID and GID ensures seamless permission handling for mounted folders while maintaining security.
Optimizing the Dockerfile and combining RUN commands can reduce the image size and improve performance. Following these steps will help you create and run a Docker container with a non-root user that aligns with your host's permissions, reducing the risk of potential security breaches and permission issues. Always prioritize security when deploying applications and containers to ensure a safe and stable environment.
Integrating Docker Compose into your workflow simplifies container management and improves the overall development experience, allowing you to focus on building your application.
Top comments (4)
Step 3 is both useless/wasteful and a security risk:
Also, rather than make the container user's UID/GID configurables, you can in many cases make the app executable as any arbitrary user (like most software on your machine) and the
--user
argument at runtime without having to rebuild the image.Hi Thomas, thanks for your remarks.
During this build process, I'm building the docker using my uid. Not just running it after the build finished (as the --user imply)
Installing using my user ID lets me simulate the standard installation of OS packages, PIP packages, and any other installation step required by the installing app/code.
Once the process runs correctly inside the docker and passes all tests, I can decide whether to install it on my real machine. If yes, I already have a perfect install done with my user from step one of the building.
The user is a parameter because all my dockers might run using the CI/CD users or other users.
And the Idea is to share files between the image and the OS without any permissions issues.
Also, login into a Linux shell using one user and running the docker as another user (using the --user) is something I'm trying to avoid.
In my usual workflow, switching a user should rebuild the image because I want to verify that the build process works for any user that should install/run the image.
I don't want to build with one user, then run with another. this might be a security risk.
And I want the CI/CD to rebuild the image anyway.
Some apps/code should be restricted from being able to change file permissions, execute external code, and more.
But this is not true for all. There are a lot of apps/codes that need to be able to change file permissions and more.
This is the first reason I'm setting the container (from the very first building step) to use my user and not the default root user.
Hello,
i'm using an image docker from liquibase.
On step 1 i got this error:
addgroup --gid Value "non root" invalid for option gid (number expected)
do you have any idea please ?