From zero to VS code: how i tame the cPanel in Docker for a real-time dev environment
Back to blog

From zero to VS code: how i tame the cPanel in Docker for a real-time dev environment

6/7/2026 · 9 min · Infrastructure

From Zero to VS Code: How I tamed cPanel in Docker and built a real-time dev environment#

Running cPanel inside a Docker container is not a simple plug-and-play task. The cPanel stack is a complex mixture of legacy system requirements, specific filesystem behaviors, and a highly sensitive service bootstrap process that interacts directly with the system manager (systemd). My primary objective was straightforward: edit cPanel core and plugin files directly in VS Code from my host machine and have those changes reflect instantly inside the container, eliminating the manual development-to-deployment copy cycle.

This article documents the end-to-end implementation of this dev environment: from actual errors to applied diagnostics, including configurations and commands required to establish a robust and reproducible setup.


1. O cenário de desenvolvimento e cPanel em containers / the development context and cPanel in containers#

cPanel was originally designed to run directly on bare-metal servers or full virtual machines where it takes exclusive control of the operating system. Running this ecosystem inside a Docker container that shares the host's kernel requires isolating its dependencies properly. Our development laboratory requires that cPanel's internal daemons (such as HTTPD, MySQL, and DNSAdmin) start correctly and that the compilation flow persists across container recreations.


2. Requisitos do systemd e pré-requisitos do Docker / systemd requirements and Docker prerequisites#

The installer and internal services of cPanel rely on systemd to manage the lifecycle of system daemons. Running systemd inside Docker containers requires specific prerequisites:

Licensing Note: cPanel requires an active license to run. For local development environments, you must use a Developer license (available on the cPanel portal) or activate the 15-day free trial period during the first bootstrap of the container.


3. Configuração da arquitetura (dockerfile e docker-compose.yml) / architecture setup (dockerfile and docker-compose.yml)#

To create a stable and reproducible environment, we use declarative configuration files. An .env environment file in the root directory should be used to load credentials without exposing secrets in code:

# Content of .env file
ROOT_PASSWORD=changeme_secure_pass_2026
SSH_PORT_CONTAINER=2222

The docker-compose.yml file encapsulates kernel permissions, cgroups mounts, and host resource limitations to prevent the container from starving the host's CPU or memory:

# docker-compose.yml
version: "3.9"

services:
  cpanel-server:
    image: almalinux:8
    container_name: cpanel-server
    privileged: true
    cgroupns: host
    deploy:
      resources:
        limits:
          cpus: '4.0'
          memory: 8gb
    volumes:
      - /sys/fs/cgroup:/sys/fs/cgroup:rw
      - cpanel_core:/usr/local/cpanel
      - type: tmpfs
        target: /run
      - type: tmpfs
        target: /tmp
    environment:
      - ROOT_PASSWORD=${ROOT_PASSWORD}
      - SSH_PORT_CONTAINER=${SSH_PORT_CONTAINER}
    ports:
      - "2222:2222"   # Custom SSH Port
      - "80:80"       # HTTP
      - "443:443"     # HTTPS
      - "2087:2087"   # WHM SSL
      - "2083:2083"   # cPanel SSL
    stop_grace_period: 60s
    security_opt:
      - seccomp=unconfined
    command: >
      /bin/bash -c "
      mkdir -p /etc && touch /etc/fstab &&
      dnf install -y openssh-server passwd systemd wget perl hostname &&
      echo 'root:${ROOT_PASSWORD}' | chpasswd &&
      echo 'Port ${SSH_PORT_CONTAINER}' >> /etc/ssh/sshd_config &&
      sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config &&
      sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config &&
      systemctl stop firewalld 2>/dev/null; systemctl disable firewalld 2>/dev/null; true &&
      systemctl stop NetworkManager 2>/dev/null; systemctl disable NetworkManager 2>/dev/null; true &&
      [ ! -f /etc/fstab ] && touch /etc/fstab &&
      systemctl enable sshd &&
      exec /usr/lib/systemd/systemd"

volumes:
  cpanel_core:

4. Resolução de conflitos de rede e pacotes no AlmaLinux 8 / resolving network and package conflicts on AlmaLinux 8#

When provisioning AlmaLinux 8 inside Docker, some legacy network tools can generate dependency errors or halt builds.

To bypass issues when disabling the firewall safely without masking critical syntax errors with the || true operator, compile the command by cleaning the standard error output:

# Safely disable firewalld
systemctl disable firewalld 2>/dev/null; true

5. O sintoma de fstab e montagens no /etc / the fstab symptom and mounts over /etc#

During the initial container boots, the cPanel installer failed due to the absence of the /etc/fstab file. This occurred because we were mounting temporary volumes or bindings directly over directories under /etc/.

This mount shadowed the container's file system during bootstrap, hiding files created earlier by the initialization script. To correct this chronological conflict, the boot script ensures the existence and touch of the /etc/fstab file immediately before handing control over to systemd execution.


6. Segurança de acesso SSH via chave pública / SSH access security via public key#

Exposing root logins via SSH using passwords passed through environment variables exposes the development container to compromise.

To harden access, we adopt exclusive SSH public key authentication, disabling password logins in the SSH daemon. The container initialization script must configure authorized keys in the root user directory:

# Configure SSH keys directory
mkdir -p /root/.ssh && chmod 700 /root/.ssh
echo "your_ssh_public_key_here" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys

# Harden sshd_config inside the container
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config
sed -i 's/^PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config

7. Instalação segura com validação GPG / secure installation with GPG verification#

Downloading installers directly from the internet and executing them with administrator privileges without verification exposes the laboratory to Supply Chain Attacks. cPanel provides GPG signatures for its official installers.

To validate the integrity and authenticity of the cPanel installer before running it in the container, use the following command flow:

# Import official cPanel public key
gpg --keyserver hkps://keyserver.ubuntu.com --recv-keys 0x2443F8B3

# Download the installer and its GPG signature
curl -o latest -L https://securedownloads.cpanel.net/latest
curl -o latest.sig -L https://securedownloads.cpanel.net/latest.sig

# Verify the file signature
gpg --verify latest.sig latest

# Run installation only if the signature is valid
sh latest

8. Arquitetura de espelhamento e volumes nomeados / mirroring architecture and named volumes#

To develop on the host in real time with changes reflecting inside the container, we need to share the cPanel data directory /usr/local/cpanel/. Mounting an empty host directory directly over the container directory via a bind mount (- /home/user/dev:/usr/local/cpanel) is destructive, as it erases all binaries written by the installer during the initial deploy.

The correct technical solution is using a named volume (cpanel_core). When the container starts for the first time, Docker performs a copy-up operation, copying the pre-existing data from the container directory to the volume storage area on the host, preserving the original data.


9. Detecção do HASH da camada de volume / detecting the volume layer HASH#

After the named volume is populated by the cPanel installation process, we need to locate where these physical files reside on the host filesystem. Using the docker inspect command, we filter active container mounts:

docker inspect cpanel-server --format '{{ range .Mounts }}{{ .Source }}:{{ .Destination }}{{ "\n" }}{{ end }}'

The output will display the absolute path of the volume on the host:

/var/lib/docker/volumes/projeto_cpanel_core/_data:/usr/local/cpanel

This absolute path is the actual source of the persisted files.


With the exact volume location identified on the host, we create a symbolic link pointing to the local user's development directory:

ln -s /var/lib/docker/volumes/projeto_cpanel_core/_data /home/user/dev/cpanel-core

By opening the /home/user/dev/cpanel-core folder in VS Code on the host, the developer can view, edit, and create code files directly. Docker's filesystem updates the content instantly for processes running inside the container.


11. O erro do overlay2: operation not permitted / the overlay2 error: operation not permitted#

During removal or reconstruction of containers running logging services (such as cPanel), the Docker Daemon can return the following critical failure:

Error response from daemon: container [HASH]: driver "overlay2" failed to remove root filesystem: unlinkat /var/lib/docker/overlay2/[HASH]/diff/usr/local/cpanel/logs/dnsadmin_log: operation not permitted

This error occurs because cPanel services configure their active log files with special security attributes, such as the immutable (+i) or append-only (+a) flags, preventing the Docker daemon from executing the unlinkat call to clean the overlay2 storage driver layer.


12. Mitigação correta contra containers zumbis / correct mitigation against zombie containers#

Many administrators attempt to force removal by stopping the Docker daemon (systemctl stop docker) and manually deleting files inside the Docker directory using commands like chattr -R -i or rm -rf /var/lib/docker/overlay2/*.

Safe recovery procedure#

To remove zombie containers locked by immutable attributes of internal files, follow this secure command playbook:

  1. Locate files with special attributes inside the running container and remove them using find combined with chattr (avoiding the unreliable chattr -R command):
   # Search and remove immutable attributes from local log files inside the container
   find /usr/local/cpanel/logs/ -type f -exec chattr -i {} \; 2>/dev/null || true
  1. Remove the container using the Docker CLI:
   docker rm -f cpanel-server 2>/dev/null || true
  1. Execute structured cleanup of orphaned resources and pending layers:
   # Remove orphaned containers and inactive volumes
   docker container prune -f
   docker volume prune -f
  1. If the container remains cached, restart the daemon cleanly to reload the storage state database:
   systemctl restart docker
   docker system prune -a --volumes -f

13. Impacto do SELinux em distribuições rhel-based / SELinux impact on rhel-based distributions#

AlmaLinux 8 operates with SELinux in Enforcing mode by default. When Docker attempts to instantiate containers running systemd with cgroups mounts, kernel security policies can block access, resulting in silent boot failures.

To mitigate SELinux conflicts without exposing the host to security risks, enable the SELinux boolean that allows containers to manage cgroups:

# Allow containers to manage cgroups under SELinux
setsebool -P container_manage_cgroup true

Additionally, when configuring volumes and shared folder mounts in docker-compose.yml, use the :z or :Z flag to update the SELinux label context on the corresponding host files.


14. Isolamento de redes e controle de portas / network isolation and port control#

Since the cPanel container listens on several sensitive system ports (such as SSH, WHM, and administration endpoints), it is critical to isolate the container network from unauthorized public access on the host.


15. Plano de backup e restauração de volumes / volume backup and restore plan#

Because modifications to cPanel core files can destabilize the environment, set up automated backup policies for the cpanel_core volume to prevent data loss.

Export volume backup#

To generate a compressed tarball containing the current state of the persisted volume, run:

# Back up the cpanel_core named volume to a local tar.gz file
docker run --rm -v cpanel_core:/source -v $(pwd)/backup:/dest almalinux:8 \
  bash -c "tar czf /dest/cpanel_core_$(date +%Y%m%d_%H%M%S).tar.gz -C /source ."

Restore backup to volume#

To restore the saved backup state back to the named volume, execute:

# Restore volume state from the backup tarball
docker run --rm -v cpanel_core:/dest -v $(pwd)/backup:/source almalinux:8 \
  bash -c "tar xzf /source/cpanel_core_backup.tar.gz -C /dest"

16. Validação do espelhamento de arquivos / validating file mirroring#

To ensure the mirroring flow is stable and persists after container shutdowns or recreations, run the following validation checks:

# 1. Create a test file using an absolute path inside the container
docker exec -it cpanel-server touch /usr/local/cpanel/test_persistence.txt

# 2. Check if the file appears in the local host directory
ls -la /home/user/dev/cpanel-core/test_persistence.txt

# 3. Recreate the environment using docker compose
docker compose down
docker compose up -d

# 4. Validate that the test file still exists inside the container post-boot
docker exec -it cpanel-server ls -la /usr/local/cpanel/test_persistence.txt

This test confirms the volume retains state and that the VS Code bridge is bidirectionally synchronized.


17. Playbook SRE de troubleshooting cPanel no Docker / SRE cPanel in Docker troubleshooting playbook#

During the operation of the local development lab, use the following quick-start resolution guide for common issues:

  # Correct ownership of the shared folder on the host
  chown -R root:root /var/lib/docker/volumes/projeto_cpanel_core/_data

18. Matriz de riscos de ambiente de desenvolvimento / development environment risk assessment matrix#

Risk EventSeverityTechnical ImpactRecommended Mitigation
Privilege Escalation (Privileged Mode)HighUsing --privileged disables container isolation, allowing malicious processes to access the host.Restrict this environment exclusively to offline local development environments.
Layer Corruption (Overlay2)HighManually editing files in the /var/lib/docker directory damages daemon metadata, breaking Docker.Never modify Docker host storage files directly; use docker system prune and restart the daemon.
SSH Key Compromise / Weak PasswordsMediumWeak root passwords or exposed private keys allow unauthorized access to the container.Disable password authentication and allow logins exclusively via public SSH keys.
Mount Conflicts in /etcMediumMounting volumes directly over /etc shadows critical OS configuration files, breaking boot.Use boot scripts that touch and verify the integrity of configurations immediately before systemd handoff.
Host Resource ExhaustionLowHeavy cPanel background services can consume 100% of host CPU or RAM, freezing the host machine.Configure explicit CPU and memory resource limits in the Docker Compose service definition.

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments