Dash0 Raises $110M Series B at $1B Valuation

  • 10 min read

How to Use sudo Inside a Docker Container

The short answer is that you probably don't need sudo. Docker containers run as root by default, and root doesn't need sudo to do anything. If you've hit sudo: command not found or sudo: PERM_SUDOERS while trying to install a package, the fix is almost never "install sudo."

What you're actually dealing with is one of three different problems wearing the same costume: a container that runs as a non-root user and needs a one-off privileged operation, a Dockerfile that switches to USER too early, or a workflow that treats containers like VMs.

Check who the container is actually running as

Before touching anything, find out which user the container runs as. Most issues with sudo in containers come from a wrong assumption about this, often dating back to how the container was started.

bash
1
docker exec my-container whoami
text
1
root

If you get root, you don't need sudo for anything inside this container. You can install packages, modify files, and bind to privileged ports directly. The default ubuntu, debian, alpine, python, and node images all run as root. Sudo isn't installed in most of them because root doesn't need it.

If you get something other than root (appuser, node, postgres, nobody), the image has a USER directive and is dropping privileges intentionally. This is the case where the sudo question actually matters, and the answer depends on whether you need elevated privileges once (debugging) or as part of the container's normal operation.

Running as root: just drop the sudo

If your Dockerfile looks like this:

dockerfile
12
FROM ubuntu:24.04
RUN sudo apt-get update && sudo apt-get install -y curl

The build fails with sudo: not found. The fix is to remove sudo:

dockerfile
12
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y curl

Every RUN instruction in a Dockerfile executes as the user set by the most recent USER instruction, and the default is root. If you haven't switched users, there is nothing for sudo to elevate to.

Non-root containers: install at build time, not runtime

The common pattern that creates "I need sudo" problems is a Dockerfile that switches to a non-root user and then expects to install packages later. Here's the broken version:

dockerfile
1234
FROM ubuntu:24.04
RUN useradd -m appuser
USER appuser
RUN apt-get install -y curl # fails: permission denied

The fix is to do all privileged operations before the USER directive, then drop privileges only for the final runtime stage:

dockerfile
1234567891011121314
FROM ubuntu:24.04
# Everything below this runs as root
RUN apt-get update && apt-get install -y curl ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN useradd -m -u 10001 appuser
# Copy app code with correct ownership in a single layer
COPY --chown=appuser:appuser ./app /home/appuser/app
# Switch to non-root only for runtime
USER appuser
WORKDIR /home/appuser/app
CMD ["./start.sh"]

This is the model Docker is built around: the image build phase has full privileges; the runtime phase doesn't need them. If your application binds to a port below 1024, expose a higher port instead (8080, 8443) and remap with -p 80:8080 at the docker run boundary, where the host has the privileges to grant low-port access.

For more complex builds where you need root-only operations interleaved with application setup, use a multi-stage build. Do the privileged work in a builder stage, then COPY --from=builder into a minimal runtime image that never had sudo in the first place.

One-off privileged operations: use docker exec --user root

If your container is already running as a non-root user and you need to run a single command as root (installing a debugging tool, fixing a permission, inspecting a file you don't own), don't install sudo. Use the --user flag on docker exec:

bash
1
docker exec --user root my-container apt-get install -y procps

Or for an interactive shell as root:

bash
1
docker exec -it --user root my-container bash

This works on every container regardless of its USER directive, doesn't require sudo to be installed, and doesn't require a password. The Docker daemon is the privileged layer here, and it can launch processes as any user it wants.

Anything you install or change this way lives only in the container's writable layer and disappears when the container is removed. That's a feature: if you find yourself wanting these changes to persist, the answer is to put them in the Dockerfile and rebuild, not to commit the running container.

When sudo actually makes sense

There is one legitimate use case for sudo in a container, and it's narrow: development environments where a human is going to interactively switch between a non-root user and root throughout a long-running session, and where you want the audit trail and selective elevation that sudo provides.

This typically looks like a developer container or a CI runner image where the default user runs the workload, but the developer occasionally needs to install something or inspect a root-owned file without dropping out of their shell. In that case:

dockerfile
1234567891011
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y sudo \
&& rm -rf /var/lib/apt/lists/*
RUN useradd -m -s /bin/bash devuser \
&& echo "devuser ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/devuser \
&& chmod 0440 /etc/sudoers.d/devuser
USER devuser
WORKDIR /home/devuser

NOPASSWD:ALL lets devuser run sudo without a password, which is necessary because there's no interactive way to set a password during image build and you don't want passwords baked into images anyway. The chmod 0440 is required: sudo refuses to read sudoers files with looser permissions.

Don't ship this to production. Passwordless sudo for a non-root user is functionally equivalent to running as root, just with extra steps. The container's security posture is the same as a root container, except now the image is bigger and the threat model is harder to reason about.

Common pitfalls

Treating containers like VMs. If your workflow involves docker exec into a running container, installing packages, editing config files, and then expecting those changes to survive, you're fighting the model. Containers are designed to be disposable. Make your changes in the Dockerfile and rebuild the image.

Forgetting that apt-get install -y sudo adds layers. Sudo and its dependencies pull in around 15-20 MB on Debian-based images, plus the /var/lib/apt/lists cache if you forget to clean it up. On a python:3.13-slim base that started at 50 MB, that's a meaningful percentage of image size for a package you may not actually need.

Running rootless Docker without realizing it. If the Docker daemon itself is running in rootless mode, the "root" user inside your container is mapped to your unprivileged user on the host. sudo inside the container still works the way it always does (root is root from the container's perspective), but operations that need real host privileges (binding to low ports on the host, mounting certain filesystems) will fail with permission errors that look unrelated. Check docker info for rootless in the security options if commands fail in ways the in-container logs don't explain.

Setting USER before file operations. If you switch to a non-root user and then COPY files, the files are owned by root and your user can't write to them. Use COPY --chown=user:group or do the copy before the USER directive.

Final thoughts

The "do I need sudo here?" question is really a "how is my container designed?" question, and getting the answer right usually means thinking about build time vs. runtime privileges and avoiding mutable state in running containers. Once your containers are running cleanly, the next thing that bites is visibility: a container that exits with a permission error in the first 200ms is hard to diagnose without Docker log capture and runtime metrics. Once the container is gone, even reading its logs depends on having captured them somewhere durable first.

Dash0's infrastructure monitoring tracks container resource usage and runtime state alongside real-time logs and distributed traces, so when a container fails on startup or a security policy blocks an operation, you see exactly what happened. Start a free trial to monitor your containers, hosts, and workloads in one place. No credit card required.