Production Setup

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, mode 0750;
  • 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.service worker listening on default,server-scans with 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_ENV is set to production
  • APP_DEBUG is disabled
  • PostgreSQL is reachable
  • Redis is reachable
  • public/storage exists and points to storage/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/serverlist is prepared with the installation owner/group and 0750 permissions
  • /install returns 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.