ServerList Production Deployment
This guide covers the production tasks required after the ServerList web installer has completed.
For the initial installation, see Installation.
PHP-FPM, scheduler and queue-worker CLI processes must all use PHP 8.4.1 or newer (PHP 8.x) with the required extensions listed in README. Check each runtime’s configuration; CLI and PHP-FPM can load different extensions.
1. Standard deployment: one setup command
The standard deployment is a single ServerList application on one Debian 13
or Ubuntu 24.04 VPS, owned by www-data:www-data, using Nginx, the distribution
PHP-FPM service, PostgreSQL, Redis, systemd and cron. PHP-FPM must be dedicated
to this installation; other PHP applications, containers, remote writers and
custom upstreams use the advanced deployment path below.
After completing the web installer and configuring the Nginx virtual host/TLS, run this command as an administrator from a trusted official release:
sudo /usr/bin/python3 -I /var/www/serverlist/scripts/standard-deployment.py setup --install /var/www/serverlist --nginx-site /etc/nginx/sites-available/serverlist
Replace the two application paths and the Nginx sites-available regular file
path if yours differ. For another supported PHP version, add --php-version 8.5.
The server needs the distribution packages python3, sudo, cron, nginx
and PHP-FPM; no Python packages or application dependencies are installed by
this command. Allow a short maintenance window: setup drains requests/workers
and gracefully drains the cron service once while adopting legacy scheduler
entries. Existing cron jobs must finish; none are force-killed. Normal updates
leave the cron service running and block only the managed application scheduler.
The command supplies and validates everything needed for standard updates:
/var/backups/serverlist, owned by the installation user/group, mode0750;- a root-owned Nginx include that closes ingress during updates except exact
GET /up, preserving existing virtual-host, TLS and upload settings; - graceful worker/FPM shutdown settings, without forced termination;
- the
serverlist-queue.serviceworker listening ondefault,server-scanswith a 60-second timeout; - a systemd-managed scheduler launched by
/etc/cron.d/serverlist; - the external updater adapter and a narrowly restricted sudoers entry.
It preserves an existing compatible queue unit and installs a managed drop-in.
A new worker is enabled and started. Existing stopped workers and disabled cron
entries remain stopped/disabled. Original configuration files are retained under
/var/lib/serverlist-update/setup-* with private file permissions. Repeating
setup for the same installation is supported; unexpected custom configuration,
symlinks, ownership conflicts and unsafe permissions are rejected instead of
being overwritten. Do not manually edit the generated adapter/drop-ins. After
intentional Nginx or PHP-FPM configuration changes or distribution service-unit
updates, rerun setup to validate and register the new configuration before the
next application update.
You do not need to write a hook or edit sudoers, cron or ingress barriers. Check the completed deployment:
sudo systemctl status serverlist-queue.service
cd /var/www/serverlist
sudo -u www-data /usr/bin/php artisan schedule:list
sudo -u www-data /usr/bin/php artisan queue:failed
The scheduler service runs briefly once per minute; being inactive between runs
is normal. If you intentionally disabled the old worker/cron entry, enable it
when ready. Keep CLI/FPM extensions and versions consistent with README.
Scheduled scans drain each round before dispatching another, enumerate in
chunks of 500, and use the same per-server overlap protection as explicit scans.
Workers must retain --queue=default,server-scans --timeout=60; queue
retry_after must remain at least 120 seconds. Custom queue names require the
advanced deployment path.
2. Existing installations and advanced deployments
An existing installation following the earlier documented Nginx, PHP-FPM,
serverlist-queue.service and direct schedule:run cron example uses the same
setup command. The command converts the legacy scheduler safely, preserves
configuration originals, and installs the update controls. It does not upgrade
application files or run migrations. See Updating for adopting the
adapter before the first update from an older release.
For Supervisor, containers, multiple applications/hosts, remote workers, custom
cron commands or reverse proxies, keep an operator-supplied
SERVERLIST_UPDATE_QUIESCENCE_HOOK. This overrides the standard adapter. See
Custom update hooks; standard customers do not need that contract.
3. Public Storage
ServerList stores public server uploads through Laravel's public filesystem.
The installer normally creates:
public/storage -> storage/app/public
Verify the link:
cd /var/www/serverlist
ls -la public/storage
If repairing an existing installation where the link is missing:
php artisan storage:link
Do not overwrite an existing unrelated public/storage path without first
investigating it.
4. PHP Upload Configuration
The active PHP-FPM configuration must allow at least:
upload_max_filesize = 5M
post_max_size = 10M
For PHP 8.4, a dedicated override can be stored at:
/etc/php/8.4/fpm/conf.d/99-serverlist.ini
Example contents:
; ServerList upload limits
upload_max_filesize = 5M
post_max_size = 10M
Restart PHP-FPM after changing the configuration:
sudo systemctl restart php8.4-fpm
Remember that PHP CLI and PHP-FPM can load different configuration files.
The ServerList installer checks the PHP configuration used by the website.
5. Nginx Upload Limit
Nginx must allow request bodies large enough for ServerList uploads.
Inside the ServerList virtual host, configure at least:
client_max_body_size 10M;
Public uploads are served through Laravel's public/storage symbolic link.
They must always be treated as static files and must never be passed to
PHP-FPM.
Place this location before the PHP location in the ServerList virtual host:
location ^~ /storage/ {
try_files $uri =404;
}
A typical PHP location can then follow normally:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.4-fpm.sock;
}
Using ^~ ensures that requests below /storage/ are handled as static files
instead of being matched by the PHP location.
Verify the Nginx configuration:
sudo nginx -t
Then reload Nginx:
sudo systemctl reload nginx
The complete upload path should therefore allow:
Nginx request body: 10 MB or more
PHP post_max_size: 10 MB or more
PHP upload limit: 5 MB or more
ServerList upload: 5 MB maximum
6. File Permissions
The PHP-FPM user must be able to write to Laravel's runtime directories:
storage/
bootstrap/cache/
It must also be able to manage public uploads under:
storage/app/public/
For a simple installation where ServerList runs entirely as www-data,
appropriate ownership can be used:
sudo chown -R www-data:www-data /var/www/serverlist
Ensure the runtime directories remain writable:
sudo chmod -R ug+rwX /var/www/serverlist/storage
sudo chmod -R ug+rwX /var/www/serverlist/bootstrap/cache
Do not use world-writable permissions such as:
chmod -R 777
For deployments with a separate application or deployment user, use shared group permissions instead.
Prepare the updater backup directory (required)
After assigning the final installation owner/group, run this privileged setup step before opening the web installer (or when repairing an existing deployment):
sudo bash /var/www/serverlist/scripts/prepare-backups.sh /var/www/serverlist
Replace both installation paths if you use a different directory. The script
creates /var/backups/serverlist outside the application with the installation
directory's owner/group and mode 0750. Run the updater as that installation
owner. A separate PHP-FPM user does not need write access to private backups.
This step is safe to repeat for the same owner and preserves existing backup
contents. It rejects symlinks, unsafe parent directories and a backup directory
belonging to another owner rather than taking over another installation's data.
Resolve such conflicts explicitly; do not use recursive ownership changes or
world-writable permissions. /var/backups must exist, be root-owned and not be
group/world writable (the normal Debian/Ubuntu setup).
The web installer performs a read-only requirement check and blocks continuation if the directory is missing or has unsafe ownership/permissions. It never runs sudo or creates privileged directories. Existing installations can run the same setup command; updater preflight checks remain unchanged, including writability and available backup space at update time.
7. Protect Sensitive Files
The public web root must be:
/var/www/serverlist/public
Never configure the web server document root as:
/var/www/serverlist
Sensitive application files and directories include:
.env
storage/
vendor/
config/
database/
These must not be directly accessible over HTTP.
The .env file contains credentials and other private configuration and must
never be publicly downloadable.
8. Production Environment
After installation, verify that ServerList is running as a production application.
Important environment values include:
APP_ENV=production
APP_DEBUG=false
Do not enable Laravel debug mode on a public production installation.
The application URL should use the final public HTTPS address.
Example:
APP_URL=https://servers.example.com
9. HTTPS
Production ServerList websites should use HTTPS.
A common setup is Nginx with Let's Encrypt and Certbot.
After HTTPS is configured:
- verify the final domain loads over HTTPS
- verify HTTP redirects to HTTPS when desired
- verify the ServerList application URL uses HTTPS
- verify uploaded images load over HTTPS
- verify login and administration pages work normally
10. Queue and Scheduler Checks
After deployment, verify the queue worker:
sudo systemctl is-active serverlist-queue.service
Expected response:
active
Check the scheduled tasks:
cd /var/www/serverlist
php artisan schedule:list
Check failed queue jobs:
php artisan queue:failed
Investigate unexpected failed jobs before considering the deployment complete.
11. Minecraft Scanner Verification
After adding and approving a Minecraft server, allow the scheduler and queue worker time to process it.
The normal status scanning flow is:
Laravel scheduler
->
Redis queue
->
ServerList queue worker
->
Minecraft status scanner
->
ServerList database
If server statuses are not updating, first check:
sudo systemctl status serverlist-queue.service
Then check:
cd /var/www/serverlist
php artisan schedule:list
php artisan queue:failed
Application logs can be inspected under:
storage/logs/
Do not expose these logs publicly.
12. Installer Lock
After a successful installation, ServerList creates its installation lock.
The installer URL:
/install
should return HTTP 404 after installation has been completed.
The temporary installer access key should also have been removed.
Do not manually remove the installation lock from a working production installation.
13. Backups
Back up ServerList before software updates or major configuration changes.
Important backup targets include:
- PostgreSQL database
.env- uploaded files under
storage/app/public - application files when appropriate
Example PostgreSQL backup:
pg_dump -U serverlist -h 127.0.0.1 serverlist > serverlist-backup.sql
Depending on PostgreSQL authentication, the command may request the database password.
Store backups outside the public web directory and preferably outside the production server.
14. Updating ServerList
After standard setup, run the official updater as the installation owner:
cd /var/www/serverlist
sudo -u www-data ./scripts/update.sh /path/to/ServerList-version.tar.gz
No hook environment variable is needed. The updater automatically uses the installed adapter. Follow Updating for normal updates and recovery. The application user has permission only to call the fixed adapter actions; it has no general sudo, shell or systemctl permission.
15. Production Checklist
Before considering a ServerList deployment complete, verify:
- website loads through the intended domain
- HTTPS is enabled
- web server document root points to
public/ APP_ENVis set to productionAPP_DEBUGis disabled- PostgreSQL is reachable
- Redis is reachable
public/storageexists and points tostorage/app/public- PHP upload limit is at least 5 MB
- PHP POST limit is at least 10 MB
- Nginx request body limit is at least 10 MB
- standard deployment setup completed successfully (or a custom adapter is configured)
- Laravel scheduler runs every minute
- Redis queue worker is active and persistent
- Laravel runtime directories have correct permissions
/var/backups/serverlistis prepared with the installation owner/group and0750permissions/installreturns HTTP 404- temporary installer credentials have been removed
- server status scanning works
- uploaded server images are publicly visible
- no unexplained failed queue jobs remain
Once these checks pass, the core ServerList production deployment is operational.
