A Guide to Avoiding Pitfalls: Configuring High-Performance Docker Rootless Mode for Independent Accounts on Debian 13 (with LAN Connectivity Guide)

docker rootless in debian 13 In production environments or cloud servers, utilizing Docker Rootless mode is increasingly preferred to isolate container environments and enhance security.

📢 Disclaimer up front: A clean, standard Debian 13 installation does not include a www user by default. The www account in this article is strictly used as an example of a service role. In practice, you can apply this workflow directly to any independent, non-root account on your system (e.g., app, deploy, or your personal account).

Based on the latest Debian 13 (Trixie), this step-by-step guide walks you through a comprehensive "check-then-configure" deployment workflow that enables native kernel overlayfs drivers and flawless LAN connectivity for Docker Rootless.


Why Debian 13 + Native Overlayfs?

In older Linux distributions, non-root users lacked the kernel privileges required for unprivileged overlay mounts. As a result, running Rootless Docker usually meant falling back to fuse-overlayfs in user space, which incurred a noticeable performance penalty.

**The great news is that Debian 13 (powered by Kernel 6.x+) natively and fully supports unprivileged overlayfs**. As long as your UID/GID mappings are configured correctly, you can run on the high-performance, kernel-level storage driver without installing any third-party bridging libraries!


🛠️ Step-by-Step Deployment Workflow

Step 1: System-Level Dependencies & User Environment Setup (As Root/Sudo)

First, we need to install the required system dependencies. Additionally, ensure that the independent non-root account you plan to configure (using www as our example) has a legitimate Home directory and an authorized Shell, as Docker Rootless stores its configurations and data independently.

# 1. Install core dependencies (uidmap for user mapping, slirp4netns for network isolation)
sudo apt update
sudo apt install -y uidmap dbus-user-session slirp4netns iptables systemd-container

# 2. If your independent account does not exist yet, run this to create it (skip if it already exists)
# sudo useradd -m -s /bin/bash www

# 3. Ensure the home directory exists with correct permissions, and fix the shell path
sudo mkdir -p /home/www
sudo chown -R www:www /home/www
sudo chsh -s /bin/bash www


Step 2: Check and Configure UID/GID Mapping

This is the absolute core requirement for enabling the native kernel overlayfs storage driver in rootless mode. We must ensure that the user has a sufficient range of subordinate UIDs and GIDs (typically at least 65,536).

1. Perform a Pre-Check First

Run the following commands to check if the system has already allocated mapping ranges for the user:

grep www /etc/subuid
grep www /etc/subgid

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 www

(Note: If the pre-check reveals multiple non-overlapping records, this is normal. The system automatically merges them, so there is no need to panic.)


Step 3: Activate Systemd Linger (Ensure Containers Survive Logout)

For independent service accounts that do not use interactive desktop logins, the system automatically terminates user-level Systemd instances as soon as you disconnect your SSH session. To ensure your containers can run persistently in the background and start automatically on system boot, you must enable Linger:

# 1. Enable user lingering
sudo loginctl enable-linger www

# 2. Check the system numeric UID of the account (usually around 1001; write this down for later)
id -u www


Step 4: Retrieve and Run the Rootless Installation Script

To install Rootless Docker, we need to invoke the official dockerd-rootless-setuptool.sh script. Depending on your server's network environment, you can choose one of the following two methods to obtain and execute it:

Method 1: Install the Complete Docker Engine & Rootless Components via APT (Highly Recommended / Fail-Safe)

To prevent errors caused by missing core Docker dependencies, run the following combined commands. This automatically configures the latest official Docker repository and installs the base engine, toolchains, and rootless extension packages all at once:

# 1. Add Docker's official GPG key and APT repository
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 2. Install the complete suite in one go
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras

Once completed, the core rootless installer script becomes available as a global system command.

Method 2: Download via Remote Script / Reverse Proxy

If you prefer not to use the APT repository, you can download the standalone script directly.

curl -fsSL https://get.docker.com/rootless | sh

curl -fsSL https://ghproxy.net/https://raw.githubusercontent.com/moby/moby/master/contrib/dockerd-rootless-setuptool.sh | sh

🚀 Initiating the Actual Installation

⚠️ Critical Pitfall Warning: Do not use standard sudo su - www to switch users here, as it drops crucial Systemd environment sessions.

Use Systemd's official machinectl tool to simulate a perfect physical login session, then execute the installation:

# 1. Spawn a proper shell session for the www user
sudo machinectl shell www@

# 2. Run the installer (If you used Method 1, run this line directly)
dockerd-rootless-setuptool.sh install

When you see the log output [INFO] Installed docker.service successfully., your Systemd-managed setup is complete.


Step 5: Persist Environment Variables

Append the following three lines to the absolute end of the /home/www/.bashrc file (Make sure to replace 1001 with the actual numeric UID you found in Step 3):

# Docker Rootless Environment Setup
export XDG_RUNTIME_DIR=/run/user/1001
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
export PATH=/usr/bin:$PATH

Save the file and apply the changes immediately: source ~/.bashrc.


Step 6: Service Management & Status Verification

1. Start and Lock the Boot Trigger

# Start the user-level Docker service
systemctl --user start docker

# Enable automatic startup on system boot
systemctl --user enable docker

2. Verify the Docker Service Status

To monitor the background daemon process owned by the user, pass the --user flag to systemctl:

systemctl --user status docker

Expected Correct Output:

● docker.service - Docker Application Container Engine
     Loaded: loaded (/home/www/.config/systemd/user/docker.service; enabled; preset: enabled)
     Active: active (running) since Tue 2026-06-02 ...

As long as you see the green active (running) status, Docker is successfully running as a persistent background daemon for this user.


🌐 Step 7: Network Integration & LAN Access Best Practices

This is a frequently overlooked performance and architectural trap.

Limitations of the Default Network Stack (slirp4netns)

By default, Docker Rootless emulates a NAT network stack in user space using slirp4netns. While this allows containers to easily access the internet via the host, it introduces two distinct pain points:

  1. Isolated from the LAN: Containers struggle to establish low-latency, bidirectional communication with other physical devices on the local area network (such as database servers, external gateways, or neighboring cluster nodes).
  2. Loss of Source IP: All incoming external traffic hitting the container is force-natted to 127.0.0.1, stripping applications of their ability to log or track real client IPs.

The Ultimate Solution: Embrace Host Network Mode

To completely bridge your container into the local area network and achieve near-native bare-metal networking performance, we highly recommend using Host network mode (--net=host) for deployments.

In Host mode, the container directly shares the network namespace of the non-root user, entirely bypassing user-space NAT translation.

Example: Deploying Nginx in Host Mode for LAN Access

docker run -d --name my-web --net=host nginx

⚠️ The Only Restriction of Host Mode on Independent Accounts: Non-root users are restricted from binding to privileged ports below 1024. Therefore, if a containerized application defaults to port 80, starting it in Host mode will throw a Permission denied error. The Fix: Simply adjust your containerized application to listen on a high port above 1024 (e.g., configure Nginx to listen on port 8080). Other devices on your local network can then access your service seamlessly at http://<HOST_IP>:8080.


For rootful and rootless docker, you should add these config to vim /etc/sysctl.d/99-docker.conf to improve the network.

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
net.core.somaxconn=65535
net.ipv4.tcp_max_syn_backlog=65535
net.netfilter.nf_conntrack_max=262144
net.ipv4.ip_local_port_range=1024 65535
net.ipv4.tcp_fin_timeout=15
net.ipv4.tcp_keepalive_time=300
net.ipv4.tcp_keepalive_intvl=30
net.ipv4.tcp_keepalive_probes=5

fs.file-max=2097152
kernel.unprivileged_userns_clone=1
user.max_user_namespaces=28633

Then activate these kernel config with command sudo sysctl --system.

Add br_netfilter to support bridge network with command sudo modprobe br_netfilter, then you can check with lsmod | grep br_netfilter, you will see:

br_netfilter
bridge

For permanent load the br_netfilter module, you need do this: vim /etc/modules-load.d/docker.conf

br_netfilter

🛡️ Step 8: Ultimate Environment Verification

1. Check Storage Driver and Rootless Status

In newer versions of Docker, the Rootless key is nested inside the Security Options block and formatted in lowercase. Use the case-insensitive flag (-i) when filtering the output:

docker info | grep -i -E "rootless|storage driver"

Expected Correct Output:

 Storage Driver: overlayfs
 Security Options:
  ...
  rootless

Note: Seeing Storage Driver: overlayfs (instead of the system-wide overlay2) alongside rootless in the security options guarantees that your setup is running with native kernel file system performance!

2. Run a Test Container

Finally, pull and run a standard test container to verify network routing, registry image pulling, and user namespace translations:

docker run --rm hello-world

When your terminal displays the classic Hello from Docker! welcome log, your independent service account officially commands its own isolated, secure, and high-performance Docker runtime environment!


Conclusion

Configuring Docker Rootless on Debian 13 boils down to two main pillars: Implementing a "check-then-configure" approach to prevent breaking existing system UID maps, and utilizing machinectl to bypass Systemd session environment initialization traps.

By executing this clean workflow, your independent service accounts can enjoy high-performance container orchestration while operating inside an absolute zero-root security sandbox.

Newest Posts