Installation
Requirements
- A fresh Ubuntu 24.04 LTS server (x86_64 or arm64)
with root access. The installer refuses hosts that already run nginx,
Postfix, MySQL or Apache sites unless you pass
--force; do not run it on a machine you use for anything else. - At least 2 GB RAM and 20 GB free disk. Rspamd, MariaDB, Apache and PHP-FPM together idle at roughly 1 GB.
- A fully qualified hostname (for example
panel.hoster.example) whoseA(and, if the server has one,AAAA) record already points to the server. This name becomes the panel URL, the mail server name clients use, and the SFTP host. - Outbound internet access for apt, the
ppa:ondrej/phpPPA and Let's Encrypt. - Ports 22, 25, 80, 443, 465, 587, 993, 995 and 8443 reachable from
the internet, plus 2087 for the admin UI (ideally only from your own
addresses). The installer configures
ufwaccordingly.
SSH lockout protection. If your
sshd_configalready containsAllowUsers,AllowGroups,DenyUsersorDenyGroups, the installer stops. panelOwl installs its ownAllowGroupsdrop-in and sshd ANDs such directives; remove yours first (the installer adds root, everysudo/admmember and the invoking user to theroot-accessgroup so nobody gets locked out).
Before you start: DNS
Create these records at your DNS provider before running the installer, so the hostname certificate can be issued immediately:
| Record | Name | Value |
|---|---|---|
| A | panel.hoster.example |
the server's public IPv4 |
| AAAA | panel.hoster.example |
the server's public IPv6 (only if the server actually has one) |
| PTR (reverse DNS) | server IPv4 | panel.hoster.example — set at your server provider;
needed for outbound mail reputation |
Set the system hostname too:
hostnamectl set-hostname panel.hoster.example.
Getting the release onto the server
Releases are published at https://panelowl.com/releases/
as a signed tarball, minipanel-<version>.tar.gz, next
to latest.json (the manifest the update feed reads). On the
new server, logged in as a user with sudo:
cd ~
curl -fsSLO https://panelowl.com/releases/latest.json
v=$(python3 -c 'import json; print(json.load(open("latest.json"))["version"])')
curl -fsSLO "https://panelowl.com/releases/minipanel-$v.tar.gz"
curl -fsSLO "https://panelowl.com/releases/minipanel-$v.tar.gz.sha256"
sha256sum -c "minipanel-$v.tar.gz.sha256"
(Or copy the file from wherever you have it with
scp minipanel-<version>.tar.gz user@server:.)
Running the installer
On the server, unpack and run the installer as root:
cd ~
tar xzf minipanel-$v.tar.gz
cd minipanel-$v
sudo ./install/install.sh --hostname panel.hoster.example --email admin@hoster.example
The installer shows every setting it is about to use and asks for confirmation (press Enter to accept the value in brackets). Options:
| Flag | Meaning |
|---|---|
--hostname FQDN |
server hostname (default: hostname -f) |
--email ADDRESS |
admin email registered with Let's Encrypt |
--php 8.3[,8.4] |
PHP versions to install (default 8.3); more can be
added later with panelctl php install |
--public-ipv4 IP |
the address clients' DNS records should point at. Detected automatically, but behind NAT the detection sees the firewall's outbound address, which may differ from the one forwarded to the server — pass the forwarded address |
--public-ipv6 IP |
likewise for IPv6, or none |
--update-url URL |
release feed the server updates from (default
https://panelowl.com/releases/; a private mirror is fine,
the manifest is signed either way) |
--skip-dns-check |
continue even if the hostname does not resolve to this server (the certificate is then requested later by the hourly timer) |
--yes |
no prompts; requires --hostname and
--email on a first install |
--force |
continue despite an existing web/mail/database stack or busy ports |
--skip-resource-check |
warn instead of abort below 2 GB RAM / 20 GB disk (test environments only) |
It takes about five minutes and is safe to re-run. What it does, in order:
- Preflight checks (OS, root, resources, existing services, sshd directives).
- Adds the PHP PPA with a pinned signing key, installs Apache, PHP-FPM, MariaDB, Postfix, Dovecot, Rspamd, Redis, fail2ban, certbot, ufw and unattended-upgrades (security updates only).
- Creates the
minipaneluser, the groupsminipanel-sftp,minipanel-ssh,minipanel-nopassandroot-access, and the directories under/var/lib/minipanel,/var/log/minipanel,/etc/minipanel. - Installs the binaries to
/usr/local/lib/minipanel/(withpanelctllinked into/usr/local/sbin/), the systemd units and the version marker. - Writes
/etc/minipanel/config.toml(hostname, admin email, detected public IPs, PHP versions, default limits). - Generates a self-signed bootstrap certificate, then writes the base
configuration of every service: default Apache vhosts (unknown hosts get
a 404 page, the hostname redirects to the panel), PHP-FPM placeholder
pools, MariaDB with secure defaults, Postfix/Dovecot/Rspamd for virtual
mailboxes with DKIM, the sshd drop-in,
/etc/cron.allow, fail2ban jails, sysctl hardening, logrotate, ufw rules. - Starts
minipaneld, runs the schema migrations andpanelctl rebuild, then requests the hostname certificate from Let's Encrypt. - Prints a summary with the panel URL and the DNS/PTR records to verify.
Behind a NAT firewall
panelOwl works fine on a private address behind a firewall that forwards ports (pfSense/OPNsense, a home router, a cloud NAT). Two things to get right:
- Give the installer the forwarded public address
with
--public-ipv4(or correct it at the prompt). The value is stored in/etc/minipanel/config.tomlaspublic_ipv4and can be changed any time (edit, thensystemctl restart minipaneld). - Forward 22, 25, 80, 443, 465, 587, 993, 995 and 8443 to the server. Let's Encrypt validation needs port 80 reachable from the internet.
From inside the same LAN, the public address only works if the firewall does NAT reflection (hairpin); otherwise use the server's LAN address, and add hosts-file entries on your PC to test client sites before their DNS exists.
After installation
Check the server's health and the certificate:
panelctl doctor
panelctl ssl status
Until the hostname certificate is issued, the panel, mail services
and default vhost use the self-signed bootstrap certificate; browsers
and mail clients will warn. The panel is also reachable by IP address
(https://<ip>:8443/) for a first look, with the same
warning. Client sites are selected by hostname, so a site opened by IP
shows the server's "Site not found" page. The hourly
minipanel-ssl.timer retries issuance once DNS is correct;
panelctl ssl retry panel.hoster.example forces an
attempt.
Create the first client account:
panelctl account create acme --domain example.com --email owner@example.com
This prints the panel URL, the account's generated password (shown once) and the DNS records the client must create. See panelctl reference for all options, and hand the client the Getting started page.
What the installer configures, by service
| Service | Configuration written | Notes |
|---|---|---|
| Apache | /etc/apache2/minipanel/ (global + one file per domain),
mod_proxy_fcgi to PHP-FPM sockets |
AllowOverride All, .htaccess works;
php_value in .htaccess does not (PHP-FPM) |
| PHP-FPM | /etc/php/<ver>/fpm/pool.d/minipanel-<account>.conf |
one pool per account and version, pm = ondemand, runs
as the account user |
| MariaDB | bind-address = 127.0.0.1, root via Unix socket, no test
DB / anonymous users |
clients connect to localhost only |
| Postfix | /etc/postfix/main.cf, master.cf, maps in
/etc/postfix/minipanel/ |
submission 587 (STARTTLS) and 465 (TLS), SASL via Dovecot, senders restricted to their own address, Rspamd milter |
| Dovecot | /etc/dovecot/conf.d/99-minipanel.conf, passwd files
/etc/dovecot/minipanel-* |
IMAPS 993 / POP3S 995 only, Maildir under
/home/<account>/mail/, quotas, Sieve files spam into
Junk |
| Rspamd | /etc/rspamd/local.d/ |
DKIM signing with per-domain keys in
/var/lib/rspamd/dkim/, 200 messages/hour per mailbox, spam
tagged (never rejected by score) |
| sshd | /etc/ssh/sshd_config.d/50-minipanel.conf |
AllowGroups root-access minipanel-sftp minipanel-ssh;
SFTP-only accounts get internal-sftp |
| cron | /etc/cron.allow = root; crontabs rendered
by the helper |
clients edit cron only through the panel |
| fail2ban | /etc/fail2ban/jail.local |
sshd, Postfix SASL, Dovecot and panel-login jails |
| ufw | ports listed under Requirements | everything else denied |
| certbot | /etc/letsencrypt/renewal-hooks/deploy/minipanel |
renewals reload Apache (and Postfix/Dovecot for the hostname certificate) |
| backups | minipanel-backup.timer,
/var/backups/minipanel/ |
nightly full archive at 03:00; see Operations |
| webmail, phpMyAdmin | Roundcube + phpMyAdmin from Ubuntu's archive, pool
minipanel-apps,
https://<hostname>/webmail/ and
/phpmyadmin/ |
see Operations |
| admin UI | minipanel-admin.service (user
minipanel-admin, 127.0.0.1:8082), Apache on 2087 |
create logins with panelctl admin create; restrict port
2087 to admin addresses |