useradd invalid user in Dockerfile
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Fixing the Docker Build Headache: Resolving useradd invalid user in Laravel Environments
As developers working with containerized applications, especially those involving complex setups like PHP frameworks and database drivers, we often run into subtle but frustrating issues during the Docker build process. One common stumbling block occurs when trying to manage file permissions and ownership inside the container, particularly when setting up non-root application users.
Today, we are diving into a specific issue encountered while setting up a Laravel environment that requires specific dependencies (like MS SQL drivers). We will examine why the useradd command fails in your Dockerfile and provide a robust solution.
The Scenario: Setting Up Custom Users in Docker
You are attempting to create a non-root user (devuser) within your PHP image to manage file permissions correctly, ensuring that subsequent commands run with appropriate ownership. This is an excellent security practice, especially when deploying applications like those built on the Laravel stack.
Here is the problematic section of your Dockerfile:
# ... (previous setup)
ARG uid
RUN useradd -G www-data,root -o -u $uid -d /home/devuser devuser
RUN mkdir -p /home/devuser/.composer && \
chown -R devuser:devuser /home/devuser
When building this with docker-compose build, you receive the error: useradd: invalid user ID '-d'. This indicates that the way the shell is interpreting the arguments passed to useradd is causing it to fail, even though the intent is clear.
Root Cause Analysis: Shell Interpretation vs. Command Structure
The error arises not necessarily because $uid is invalid, but how the specific combination of flags (-G, -o, -u, -d) and the variable substitution interact within a single RUN command in a Dockerfile.
When you use shell scripting inside a RUN instruction, especially when mixing variables and positional arguments for system commands like useradd, subtle syntax errors can cause the execution to fail prematurely. The error message points directly to an issue with the -d flag (defining the home directory), suggesting that the preceding variable substitution is being misinterpreted by the shell or the command itself in this specific context.
The core fix is to ensure that system commands are executed sequentially and robustly, often separating complex operations into distinct steps or ensuring environment stability.
The Solution: Refactoring for Robustness
Instead of trying to cram all user creation parameters into one extremely long command, we can break down the process. More importantly, when dealing with UIDs in Docker builds, it is often safer to use explicit numbers rather than relying solely on dynamic variables passed from ARG unless you are absolutely sure of the shell context.
For this specific scenario, since you are passing $uid, let's ensure that we handle permissions explicitly and separate the creation from the ownership setting. A more reliable approach involves ensuring the user ID is set correctly and handling directory creation separately.
Here is the refactored approach that resolves the invalid user ID error:
FROM php:7.2-apache
ENV ACCEPT_EULA=Y
# Microsoft SQL Server Prerequisites (Keep this section as is)
RUN apt-get update \
&& curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - \
&& curl https://packages.microsoft.com/config/debian/9/prod.list \
> /etc/apt/sources.list.d/mssql-release.list \
&& apt-get install -y --no-install-recommends \
locales \
apt-transport-https \
&& echo "en_US.UTF-8 UTF-8" > /etc/locale.gen \
&& locale-gen \
&& apt-get update \
&& apt-get -y --no-install-recommends install \
unixodbc-dev \
msodbcsql17
RUN docker-php-ext-install mbstring pdo pdo_mysql \
&& pecl install sqlsrv pdo_sqlsrv xdebug \
&& docker-php-ext-enable sqlsrv pdo_sqlsrv xdebug
# --- Refactored User Setup ---
ARG uid=1000 # Set a sensible default if the build fails to pass it
ARG group_prefix=devuser
# 1. Create the user using explicit, clean parameters
RUN useradd -m -g $uid -s /bin/bash ${group_prefix}
# 2. Ensure ownership of application directories
RUN mkdir -p /home/${group_prefix}/.composer && \
chown -R ${group_prefix}:${group_prefix} /home/${group_prefix}
Why This Works Better
- Explicit Defaults: I added a default value (
ARG uid=1000) to ensure that if the dynamic variable is somehow missing or invalid during the build, the command defaults to a standard UID, preventing immediate failure. - Simplified
useradd: By simplifying the flags and ensuring the-m(create home directory) flag is used, we let the system handle the creation more cleanly. We use the passed variable ($uid) directly for group and primary group settings, which is often less ambiguous than trying to force specific secondary groups likewww-datain a single line. - Separation of Concerns: We separated the user creation step from the directory ownership steps. This makes debugging far easier. If the user creation fails, we know exactly where to look.
Conclusion: Building Reliable Laravel Images
Managing permissions and users inside Dockerfiles is a critical skill for maintaining secure and reproducible environments, especially when setting up complex stacks like those used in modern PHP frameworks. Whether you are deploying a standard application or leveraging tools within the Laravel ecosystem, correctness in your container setup directly impacts security and stability.
By adopting these refactoring techniques—focusing on clear shell execution and robust variable handling—you ensure that your Docker builds remain predictable and reliable. Remember, good infrastructure starts with clean, well-tested build scripts, just as maintaining a solid application requires sound architectural principles, much like the principles espoused by organizations like laravelcompany.com. Happy building!