Configuration (deploy/.env)
deploy/.env holds what the stack needs before the panel can run: the version and channel, domains and certificates, the database, the mail relay and the owner’s first sign-in. Everything else is a panel setting, changed under Settings.
provision/setup.sh writes the file on a fresh server and never overwrites it. Some variables are read only once, on the panel’s first boot, to fill in a setting. Their comments below say so.
This page lists all 40 variables of deploy/.env.example, in its own groups and order. The 9 variables marked optional are commented out in the example. Remove the # to set one.
Version and update channel
Section titled “Version and update channel”| Variable | Example value | What it does |
|---|---|---|
WPL7_SOURCE |
image |
What is installed, and where it comes from. provision/update.sh maintains all of these; there is normally no reason to edit them by hand.WPL7_SOURCE=image: pull the released panel image named below (the default)WPL7_SOURCE=build: compile the panel from this checkout instead (provision/build.sh sets this; provision/deploy.sh is the update path for it) |
WPL7_VERSION |
empty | The version this install reports and compares against releases. In image mode it must name a published release - nothing here invents one. |
WPL7_IMAGE_TAG |
empty | The tag those images were published under. Same as WPL7_VERSION for a release; edge for the rolling build of main, which calls itself 0.x.y-edge.<commit> but is published under a tag that moves. |
WPL7_CHANNEL |
stable |
stable = releases | edge = the rolling build of main. Only changes which release the panel offers to update to; it is applying an update that changes what is installed. |
WPL7_PANEL_IMAGEoptional |
ghcr.io/andyfo/wpl7/panel |
Override to run a fork’s or a mirror’s images. Repository only, without the tag. |
WPL7_WORDPRESS_IMAGEoptional |
ghcr.io/andyfo/wpl7/wordpress |
See WPL7_PANEL_IMAGE above. |
WPL7_REPOoptional |
andyfo/wpl7 |
Where releases are looked up (GitHub owner/name). Change it with the images above. |
WPL7_GITHUB_TOKENoptional |
empty | Fine-grained read-only token, only needed while the repository is private. It also makes the panel’s hourly check free of GitHub’s rate limit, which an anonymous one is not. |
WPL7_COMMUNITY_URLoptional |
https://wpl7.com/community |
The project’s community: linked from the panel’s About and Support pages, and where “a question or an idea” in its feedback dialog is posted. Empty it to offer neither. |
| Variable | Example value | What it does |
|---|---|---|
SERVER_ROLE |
main |
main = panel + sites (the default single-server install) | worker = sites only, driven by the central panel over SSH (compose.sh then skips the panel container). |
Identity / domains / TLS
Section titled “Identity / domains / TLS”| Variable | Example value | What it does |
|---|---|---|
PANEL_DOMAIN |
panel.example.com |
Hostname of the admin panel (A record → this server). |
DEV_DOMAIN |
dev.example.com |
Dedicated dev domain. Every new site defaults to <slug>.${DEV_DOMAIN}. Requires a wildcard DNS record: *.dev.example.com → this server. |
ACME_EMAIL |
[email protected] |
Let’s Encrypt account email. |
TLS_MODE |
letsencrypt |
letsencrypt = production certs | staging = LE staging (testing) | none = plain HTTP (local dev) NOTE: TLS_MODE only decides http vs https; it does NOT pick the ACME CA. For staging certificates set ACME_RESOLVER=letsencrypt-staging as well. |
ACME_RESOLVER |
letsencrypt |
Which ACME CA issues certificates: letsencrypt | letsencrypt-staging. Applies to the panel, custom-domain sites AND the dev wildcard certificate. |
SERVER_PUBLIC_IP |
empty | Public IPv4 of this server; used for DNS preflight warnings (auto-detected by setup.sh). |
DNS: wildcard cert for dev sites, panel-managed records (optional, recommended)
Section titled “DNS: wildcard cert for dev sites, panel-managed records (optional, recommended)”| Variable | Example value | What it does |
|---|---|---|
DNS_PROVIDER |
empty | Cloudflare is set up in the panel, under Settings → DNS: its API token (Zone → Zone → Read and Zone → DNS → Edit), and per server whether dev sites share one *.${DEV_DOMAIN} wildcard certificate over DNS-01 instead of one HTTP-01 certificate each. The same token lets the panel manage per-site records across servers and publish SPF/DKIM/DMARC (docs/dns.md).CF_DNS_API_TOKEN below is read ONCE, on the panel’s first boot, to fill that in - after that Settings → DNS owns the token, and editing it here changes nothing.DNS_PROVIDER is the provider Traefik’s DNS-01 resolver uses; empty means cloudflare. Another provider (https://doc.traefik.io/traefik/https/acme/#providers, e.g. hetzner) works for the wildcard certificate with its credentials here, but the panel writes records through Cloudflare only. |
CF_DNS_API_TOKENoptional |
empty | See DNS_PROVIDER above. |
HETZNER_API_KEYoptional |
empty | See DNS_PROVIDER above. |
DO_AUTH_TOKENoptional |
empty | See DNS_PROVIDER above. |
Filesystem
Section titled “Filesystem”| Variable | Example value | What it does |
|---|---|---|
SRV_ROOT |
/srv |
All persistent state lives under this root (sites, backups, mysql, traefik, panel, plugins). |
BACKUP_ROOToptional |
/mnt/backups |
Optional: keep THIS server’s backups somewhere other than ${SRV_ROOT}/backups - a second disk, a large volume. Setting it makes compose.sh mount that path into the panel container at the identical path (docker-compose.backup-root.yml), which is the only way the panel can write there; recreate the panel afterwards (./provision/compose.sh up -d panel).Needed only on the machine running the panel. Worker servers take any path straight from Panel → Backups → Storage. And if the disk is not in use yet, the simplest option is to mount it AT ${SRV_ROOT}/backups and leave this unset. |
MariaDB
Section titled “MariaDB”| Variable | Example value | What it does |
|---|---|---|
MARIADB_ROOT_PASSWORD |
empty | |
MARIADB_BUFFER_POOL |
256M |
|
MARIADB_MAX_CONNECTIONS |
300 |
Outbound email (wp_mail)
Section titled “Outbound email (wp_mail)”| Variable | Example value | What it does |
|---|---|---|
MAIL_HOSTNAME |
mail.example.com |
PHP mail() works on every site out of the box: the site image relays through the per-server postfix container, which signs with DKIM and delivers. Panel → Mail shows health, full traffic and the DNS records each customer domain needs (docs/mail.md).Hostname postfix announces (set an A record + reverse DNS to this server for direct mode). Any name you control - it need not sit under the panel’s domain. This is the default: a name set in Panel → Mail → Setup guide overrides it (via /srv/mail/relay.env) until you reset it there. See “Changing the mail hostname” in docs/mail.md. |
SMTP_RELAYHOST |
empty | Smarthost mode (recommended): relay through one SMTP account, e.g. [smtp.eu.mailgun.org]:587 Leave empty for direct MX delivery (your VPS provider must allow outbound port 25; set rDNS + SPF or expect spam folders — see docs/mail.md). |
SMTP_USERNAME |
empty | See SMTP_RELAYHOST above. |
SMTP_PASSWORD |
empty | See SMTP_RELAYHOST above. |
| Variable | Example value | What it does |
|---|---|---|
PANEL_SESSION_SECRET |
empty | Cookie/session signing secret (openssl rand -hex 24). |
PANEL_ADMIN_USER |
admin |
The owner account, which the FIRST boot creates from these (the name is seeded, the password seeds the hash). Afterwards change either in the panel UI - Users → your account, where everyone else gets an account too - and editing these lines has no effect. The one exception: an owner password hash blanked by hand is re-seeded from PANEL_ADMIN_PASSWORD (docs/troubleshooting.md). |
PANEL_ADMIN_PASSWORD |
empty | See PANEL_ADMIN_USER above. |
Defaults seeded into panel settings on first boot (editable in the UI afterwards)
Section titled “Defaults seeded into panel settings on first boot (editable in the UI afterwards)”| Variable | Example value | What it does |
|---|---|---|
BACKUP_CRON |
0 3 * * * |
|
BACKUP_RETENTION |
10 |
|
WP_DEFAULT_PHP |
8.3 |
|
WP_PHP_VERSIONS |
8.2,8.3,8.4,8.5 |
|
WP_DEFAULT_LOCALE |
en_US |
Any wp.org locale code (cs_CZ, de_DE_formal, pt_PT_ao90 …); the panel’s language picker offers WordPress’s full list. Editable later under Settings → Sites. |
WP_DEFAULT_PLUGINS |
empty | Comma-separated wp.org plugin slugs preselected for new sites (optional). WordPress’s own bundled plugins (Akismet, Hello Dolly) are removed from every new site regardless. |
Recipe catalog
Section titled “Recipe catalog”| Variable | Example value | What it does |
|---|---|---|
WPL7_CATALOG_URL |
empty | Plugin recipes (how the panel activates ACF PRO, Breakdance, … licenses) ship with the panel and are also published as a signed public catalog the panel fetches hourly, so a new or corrected recipe reaches this install without a software update. Empty = the official catalog. off = never fetch; use only the recipes bundled with this version. A fork that publishes its own catalog puts its index URL here and its public key below. |
WPL7_CATALOG_PUBLIC_KEY |
empty | Ed25519 public key (SPKI) the index must be signed with; empty = the official key. Put just the base64 body of the PEM on this one line - a multi-line PEM does not survive an env file - or the PEM with its line breaks written as literal \n. |