ispconfig-docker

ISPConfig as a single Docker container

The full ISPConfig 3.2 "perfect server" — nginx, PHP-FPM, MariaDB, mail, DNS, FTP and CalDAV — packaged into one systemd container. Build once, run once, and the panel installs itself on first boot.

systemd PID 1 Debian 12 / Ubuntu 22.04 / 24.04 PHP 8.3 + 8.4 unattended autoinstall

github.com/taavityrnpuu/ispconfig-docker

What is it

ISPConfig is a hosting control panel for managing websites, email, DNS and FTP accounts on Linux servers. Installing it normally means running the "perfect server" guide against a bare OS. This project collapses all of that into a single Docker image that runs systemd as PID 1 and executes the official ISPConfig autoinstaller unattended on first boot.

Everything inside one container

  • Web: nginx + nginx-extras, PHP-FPM 8.3 & 8.4 (Sury)
  • Database: MariaDB
  • Mail: Postfix, Dovecot, Rspamd, ClamAV, Postgrey
  • DNS: BIND9
  • FTP: Pure-FTPd (MySQL backend)
  • Webmail & DB admin: Roundcube, phpMyAdmin
  • CalDAV/CardDAV: Radicale
  • Security: Jailkit, Fail2ban, Memcached

Quick start

git clone https://github.com/taavityrnpuu/ispconfig-docker.git
cd ispconfig-docker
docker compose build --build-arg BASE_IMAGE=ubuntu:24.04
docker compose up -d

First boot runs the ISPConfig autoinstaller and takes a few minutes. Watch it with:

docker compose logs -f ispconfig

When it's done, open https://<host>:8080 and log in.

Default credentials
AccountUserPassword
ISPConfig paneladminadmin
MySQL rootrootrootpw
ISPConfig DBispconfigisppw
Radicaleadminadmin

Tutorial

1. Prerequisites

2. Get the source

git clone https://github.com/taavityrnpuu/ispconfig-docker.git
cd ispconfig-docker

3. Configure (optional)

Set credentials and the distro in docker-compose.yml. The key environment variables:

environment:
  - MYSQL_ROOT_PASSWORD=rootpw      # change me
  - ISP_DB_PASSWORD=isppw           # change me
  - ISP_CONFIG_VERSION=3.2.12p1

Also set a real hostname in hostname: (it's used by mail/DNS).

4. Build

docker compose build --build-arg BASE_IMAGE=ubuntu:24.04

BASE_IMAGE accepts debian:12, ubuntu:22.04 or ubuntu:24.04.

5. Run

docker compose up -d

On first boot the container starts MariaDB, then runs the ISPConfig installer against /etc/ispconfig/autoinstall.ini. This takes several minutes. The install is one-shot — it marks itself done and won't run again on restart.

6. First login

Browse to https://<host>:8080 (the panel runs on its own port, not 443). Accept the self-signed cert, log in as admin / admin, and change the password immediately under System → Users.

7. Create your first site

In the panel: Sites → Website → Add new website. Choose a domain, pick PHP 8.3 (or 8.4), and the panel writes the nginx vhost + FPM pool for you. Create mail/DNS/FTP accounts the same way from their respective menus.

Configuration reference

Build arguments

ArgumentDefaultPurpose
BASE_IMAGEdebian:12Base OS (debian:12 / ubuntu:22.04 / ubuntu:24.04)
PHP_VERSION8.4Extra Sury PHP version, decoupled from the base
ISP_PHP_VERSION8.3The distro-default PHP ISPConfig's OS profile manages
MYSQL_ROOT_PASSWORDrootpwMariaDB root password
ISP_DB_PASSWORDisppwISPConfig database password
ISP_CONFIG_VERSION3.2.12p1ISPConfig release to install

Exposed ports

ServicePort(s)
ISPConfig panel8080
SSH2222
HTTP / HTTPS80 / 443
SMTP / submission / SMTPS25 / 587 / 465
IMAP / IMAPS143 / 993
POP3 / POP3S110 / 995
FTP (+ passive)21 / 20000-21000
DNS (BIND)53 tcp + udp
Radicale5232

Persistent volumes

VolumeMount
www/var/www (sites)
vmail/var/vmail (mailboxes)
db/var/lib/mysql (MariaDB)
journal/var/log/journal

FAQ

Why one container instead of one per service?

ISPConfig assumes a single host running all services together — its installer and panel manage system services directly. Splitting them into microservices would fight the panel rather than help it. Running the whole "server" as one privileged systemd container mirrors a dedicated VPS and keeps the panel's expectations intact.

Is it production-ready?

It runs a genuine ISPConfig install with all its services, but the default credentials are placeholders and it's privileged: true. Change the passwords, set a real hostname, restrict the exposed ports, and back up the named volumes before relying on it.

How do I change the PHP version?

PHP_VERSION adds an extra Sury PHP you can select for sites. ISP_PHP_VERSION must stay at the distro default (8.3 on ubuntu:24.04, 8.2 on debian:12, 8.1 on ubuntu:22.04) because ISPConfig's OS profile hardcodes that version for its own pools and sockets.

How do I upgrade ISPConfig or the container?

Data lives in named volumes, so a rebuild + docker compose up -d recreates the container without touching sites, mail or the database. To bump ISPConfig, raise ISP_CONFIG_VERSION and rebuild; the one-shot install marker keeps it from re-running on an existing volume.

Why do I need privileged: true?

systemd-as-PID-1 needs SYS_ADMIN/NET_ADMIN and an unconfined seccomp profile, and BIND/Fail2ban/pure-ftpd need networking and kernel access. This is the cost of running a full server image; it's not suitable for untrusted multi-tenant hosts.

What about Let's Encrypt certificates?

ISPConfig can manage Let's Encrypt per-site once the panel is up. The panel's own listener starts with a self-signed cert; you can replace it in the panel's SSL settings or front it with a reverse proxy.

Known issues

Fixed in the Dockerfile — no action needed
These two are the reason the build exists in its current shape.
Panel 502: ISPConfig hardcodes the distro-default PHP

ISPConfig's OS profile picks one "default" PHP version per base (8.3 on ubuntu:24.04) and writes its FPM pools + nginx sockets for that version. An image that installed only a newer Sury PHP (e.g. 8.4) would leave /var/lib/php8.3-fpm/apps.sock missing and the panel would 502.

Resolution: the Dockerfile installs ISP_PHP_VERSION (the distro default) alongside PHP_VERSION.

Panel 500: "temp is not writable" from Sury's php-fpm hardening

Sury's php-fpm systemd units ship ProtectSystem=full, which mounts /usr read-only in the FPM worker's namespace. Since ISPConfig lives in /usr/local/ispconfig and writes interface/temp + server/temp, the panel's PHPIDS check fails and the UI throws a 500 — while the same path is writable from the CLI.

Resolution: the build drops a systemd override ProtectSystem=no onto both php-fpm services.

Port 53 already in use (DNS)

On hosts running systemd-resolved, the stub listener on 127.0.0.53 can conflict with BIND's wildcard bind on port 53. Disable it (systemctl disable --now systemd-resolved) or stop mapping port 53 if you don't need container DNS.

First boot is slow

The first up -d downloads and compiles the whole ISPConfig stack (several minutes). Subsequent boots are fast — only the one-shot install runs on the first start.

Source

Everything lives in one repo: github.com/taavityrnpuu/ispconfig-docker. Open an issue there for bugs or questions.