Running on Docker and Railway
bipanel runs on Docker and Railway as one container: Apache, PHP-FPM, MariaDB and the panel ship in the same image, and persistent data lives in the /data volume. It suits websites, databases and apps; email, a DNS server, FTP and system-level tools exist only on a server installation.
On this page
#What container mode is
In a container installation bipanel is a single Debian-based image. It contains Apache 2.4 (mpm_event), PHP-FPM for PHP 8.2, 8.3 and 8.4, an embedded MariaDB, Node.js 22, Python 3, phpMyAdmin, git, rclone and certbot. The panel process starts Apache, PHP-FPM and MariaDB itself. Apache listens on the port in the PORT variable (8080 by default), and the panel and phpMyAdmin are reached through that port.
Unlike a server installation there are no separate admin and user ports. On the same address:
- Server Admin:
https://<panel address>/admin(first useradmin) - User panel:
https://<panel address>/panel
System users, Apache virtual hosts and PHP-FPM pools are not kept inside the container; they are rebuilt from the panel database in /data on every start. As long as the volume is attached, deleting and recreating the container or redeploying it is safe.
Licensing works the same way: without a license key the Community edition runs up to 3 hosting accounts, and Pro features need a Pro license. See Licensing and Pricing.
#Docker Compose with Caddy
On your own VPS you need Docker Engine 24 or later and Docker Compose v2. 2 GB of RAM works, 4 GB is recommended. You need a domain for the panel with an A record pointing at the server, and ports 80 and 443 open.
The release package includes Dockerfile, docker-compose.yml and deploy/caddy/Caddyfile. Get the package and the current commands from the Download page. After unpacking it, create a .env file in the folder with BIPANEL_PANEL_HOST, BIPANEL_ADMIN_PASSWORD and ACME_EMAIL, then run:
docker compose up -d --build
docker compose logs -f bipanel
The first start prepares the MariaDB data directory, so the panel can take a few minutes to come up. The image health check polls /api/health and allows 180 seconds for startup.
#What Caddy does
The compose file defines two services: bipanel, which publishes no ports, and caddy in front of it (80, 443 and 443/UDP). Caddy obtains a Let's Encrypt certificate for each domain on its first request, but only for domains registered in bipanel: before requesting a certificate it asks the panel's internal check endpoint. Plain HTTP is redirected to HTTPS. This setup sets BIPANEL_SSL_MODE=edge and BIPANEL_TRUST_PROXY_HOPS=2 (Caddy plus Apache), so brute-force protection sees the real client IP.
If you use another reverse proxy instead of Caddy, forward the X-Forwarded-For and X-Forwarded-Proto headers, leave the Host header unchanged, allow WebSocket upgrades (for the web terminal and live logs) and set BIPANEL_TRUST_PROXY_HOPS to the number of proxies plus one.
#Deploying on Railway
On Railway bipanel runs as a single service, and Railway's edge terminates TLS.
- Create a new project and pick the Docker image shown on the Download page, or your repository with the bipanel source code. The repository's
railway.jsonsets/api/healthas the health check with a 300-second timeout. - Attach a volume mounted at
/data. This is required; without it every deploy wipes all data. - Set the
BIPANEL_ADMIN_PASSWORDvariable. Optionally addRAILWAY_API_TOKEN. - Generate a public domain for the service and open Server Admin at
/admin.
Railway only routes traffic to domains attached to the service. With RAILWAY_API_TOKEN set, bipanel attaches the domains you add in the panel to the Railway service itself and shows the DNS records you need to create; without it you add them in the Railway dashboard. Your Railway plan decides how many domains a service can have. Because Railway only accepts HTTP(S) traffic, services such as email, DNS and FTP cannot run there.
#Environment variables
| Variable | Default | Description |
|---|---|---|
BIPANEL_ADMIN_PASSWORD | random | First admin password. If unset, a random one is generated, printed once in the log and saved to /data/initial-admin.txt. Used only on the first start. |
BIPANEL_ADMIN_USER | admin | First admin's username. |
BIPANEL_ADMIN_EMAIL | none | First admin's email address. |
BIPANEL_PANEL_HOST | RAILWAY_PUBLIC_DOMAIN on Railway | The panel's domain. In a container the hostname cannot be changed from the panel; this variable sets it. |
BIPANEL_SSL_MODE | edge on Railway, acme on Docker | edge: an outer layer terminates TLS. acme: the panel obtains Let's Encrypt certificates itself with certbot. off: no certificate management. |
BIPANEL_TRUST_PROXY_HOPS | 2 on Railway, 1 on Docker | Number of trusted proxies, Apache included. |
PORT | 8080 | The port Apache listens on; Railway sets it for you. |
BIPANEL_MYSQL_URL or MYSQL_URL | none | An external MySQL/MariaDB server instead of the embedded MariaDB (e.g. mysql://root:password@mysql:3306). |
BIPANEL_MYSQL_BUFFER_POOL | 128M | InnoDB buffer pool of the embedded MariaDB; lower it on small plans. |
BIPANEL_MAX_UPLOAD_MB | 1024 | File Manager upload limit in MB. |
RAILWAY_API_TOKEN | none | Lets bipanel attach domains to the Railway service and update itself from the panel. |
BIPANEL_SECRET | /data/secret.key | The panel's secret key (at least 32 characters). If unset, it is generated on first start and stored in the volume. |
#Persistent data: /data
The only persistent directory in the container is the /data volume:
| Path | Contents |
|---|---|
/data/bipanel.db | Panel database: accounts, domains, settings |
/data/home/<account> | Account files |
/data/mysql | Embedded MariaDB data directory |
/data/backups, /data/logs, /data/ssl, /data/jobs | Backups, logs, certificates, job records |
The installation identity and license settings live in this volume too. If the volume is lost, the installation starts from scratch and you have to enter the license key again. Sending account backups to a remote target with rclone is recommended (see Backups and migration).
#What is different in container mode
The following are not available in a container; the panel shows these screens as disabled, with the reason:
| Feature | In a container |
|---|---|
| Mail server and webmail | Not available; email needs a server installation |
| Authoritative DNS server | Not available; the DNS zone editor gives record suggestions and the Cloudflare integration can be used |
| FTP, SSH and SFTP | Not available; use the File Manager, the web terminal and Git deployment |
| Firewall and nftables DDoS protection | Not available; the platform manages networking. In-panel brute-force protection works |
| Disk quotas | Not enforced; disk usage is only monitored |
| Per-account CPU and memory limits, account isolation | Not available (no systemd); apps only get a process count limit |
| Disk Manager | Not available; the platform manages disks and volumes |
| Automatic OS security updates | Not available; packages come with the image |
| Server reboot, root password, DNS resolvers | Not available; managed from the platform dashboard |
| Remote desktop (Pro) and Podman containers | Not available |
What works: accounts and packages, domains, PHP 8.2–8.4, MySQL/MariaDB and phpMyAdmin, Node.js and Python apps, Git deployment, the App Installer, cron (through the panel's own scheduler), backups, the File Manager, the web terminal and the API. The installation report is always on in containers as well; what is sent is shown under Server Admin → Installation Report.
#Updating
A container installation does not update the panel in place the way a server installation does:
- Docker: pull the new release's image, or rebuild from the new release package, and recreate the container. Data stays in the volume.
- Railway: with
RAILWAY_API_TOKENset, an update started from Server Admin → Updates switches the service to the new release's image and starts a redeploy. Without it, redeploy from the Railway dashboard.
See Updates for release channels.
#Resetting the admin password
Changing BIPANEL_ADMIN_PASSWORD later does not reset the password. Run the reset tool inside the container (add --disable-2fa to also turn off two-factor authentication):
docker compose exec bipanel node /opt/bipanel/deploy/tools/reset-admin.mjs admin 'NewPassword123!'
The command-line tool is in the image too: docker compose exec bipanel bipanel account list (the token is read from /data/cli.token).
Something missing or wrong on this page? Let us know.