How to Run a Full OS in Docker Without Specifying a Command: Systemd/Init Solutions


1 views

When working with Docker, you'll quickly notice that containers require an explicit command to execute. The standard syntax:

docker run ubuntu /bin/bash

This becomes problematic when you want to run a full operating system with its standard initialization system. The container exits immediately when the specified command terminates.

Most base OS images (like Ubuntu, CentOS, or Debian) are intentionally minimal. They don't include init systems because:

  • Docker recommends single-process containers
  • Init systems require privileged operations
  • They add unnecessary overhead for most container use cases

Here are several approaches to achieve full OS functionality:

1. Using systemd in Privileged Mode

For modern distributions using systemd:

docker run -d --privileged \
    -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
    --name full_os \
    centos:7 \
    /usr/sbin/init

2. For Older Systems with SysVinit

docker run -d --privileged \
    --name legacy_os \
    centos:6 \
    /sbin/init

3. Using Supervisord as Lightweight Alternative

Create a Dockerfile:

FROM ubuntu:20.04
RUN apt-get update && apt-get install -y supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
CMD ["/usr/bin/supervisord"]

When running full OS containers:

  • Always use --privileged flag for init systems
  • Mount necessary system directories (/sys, /proc, etc.)
  • Be aware of security implications
  • Consider whether you truly need a full OS vs specific services

Here's how to run an SSH-accessible full OS container:

docker run -d --privileged \
    -p 2222:22 \
    -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
    --name ssh_container \
    ubuntu:20.04 \
    /usr/sbin/init

Then inside the container:

apt-get update && apt-get install -y openssh-server
systemctl enable ssh
systemctl start ssh
passwd root  # Set your password

When working with Docker, the default behavior requires you to specify an entrypoint command that keeps the container running. This becomes problematic when you need a full operating system environment with init systems like systemd or sysvinit.

The error Error: create: No command specified occurs because Docker expects a foreground process to keep the container alive. A traditional OS expects its init system to be PID 1 and manage services.

Here's how to properly run a full OS container with systemd:

docker run -d --name full_os \
  --privileged \
  --tmpfs /run \
  --tmpfs /run/lock \
  -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
  ubuntu:latest \
  /sbin/init
  • --privileged: Grants full system capabilities
  • --tmpfs: Creates temporary in-memory filesystems required by systemd
  • -v /sys/fs/cgroup: Mounts the control group filesystem
  • /sbin/init: Starts the systemd process as PID 1

If you don't need full systemd functionality, Supervisord can manage multiple services:

FROM ubuntu:latest
RUN apt-get update && apt-get install -y supervisor
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]

Running full OS containers in Docker has several implications:

  • Security: Privileged containers have root access to the host
  • Performance: Systemd adds overhead compared to single-process containers
  • Portability: Some systemd features may not work in containerized environments

Here's a complete example using CentOS:

# Create Dockerfile
FROM centos:7
RUN yum -y install systemd; yum clean all; \
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;

# Build and run
docker build -t centos7-systemd .
docker run -d --name centos7 \
  --privileged \
  --tmpfs /run \
  --tmpfs /run/lock \
  -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
  centos7-systemd \
  /usr/sbin/init

After starting the container, verify systemd is running:

docker exec -it centos7 systemctl status