Start

Configuration

The panel reads its configuration from /opt/server-manager/.env. This page lists every setting, then covers putting the panel on its own domain with HTTPS.

The .env file

The file is passed to the container by Docker Compose and holds the admin and MySQL credentials, so keep it chmod 600 and never commit it. These values are never returned by the panel's API. After editing, apply the changes with docker compose up -d.

VariableDefaultPurpose
SM_ADMIN_EMAIL—Login email.
SM_ADMIN_PASSWORD—Login password, plain text or a bcrypt hash ($2y$…). Generate a hash with htpasswd -bnBC 12 "" 'your-pass' | tr -d ':\n'.
SM_ADMIN_NAMEAdministratorDisplay name.
SM_PORT8090Port the panel listens on (host networking).
SM_BIND0.0.0.0Listen address. Set to 127.0.0.1 once the panel domain works, so it's only reachable over HTTPS.
SM_PANEL_DOMAIN_PORT2509Port the panel domain is served on, e.g. https://panel.example.com:2509. Must differ from SM_PORT. The panel is never served on 80/443 for that domain, so you can gate it with a firewall rule. Set to 443 to use the standard HTTPS port instead.
SM_INSTALL_DIR/opt/server-managerHost path of the git checkout. Used by Settings → Update now.
SM_HOME_PATH/homeFile manager jail, and parent of the deploy user's home.
SM_DEPLOY_USERdeployLinux user that owns projects and runs PHP-FPM, workers and cron.
SM_PHP_VERSIONS8.3,8.4Fallback list if PHP detection on the host fails.
SM_MYSQL_HOST / _PORT127.0.0.1 / 3306Host MySQL server.
SM_MYSQL_USER / _PASSWORDsm_admin / —Admin account used to create databases and users (created by the provision script).
SM_CERTBOT_EMAIL—Default Let's Encrypt contact (can be changed in Settings).
APP_KEYautoLaravel encryption key. Encrypts stored DB passwords, webhook secrets and 2FA secrets. Left empty, one is generated into the data volume on first boot. Don't change it once data exists.
APP_URL—Public panel URL. Update it when you add a panel domain. Webhook URLs are built from the address you're browsing the panel on, so open it via the domain before copying one.
APP_TIMEZONEUTCTimezone for the UI's cron previews (match the server's).
SESSION_SECURE_COOKIEfalseSet to true once you only use HTTPS.
SESSION_LIFETIME240Minutes before an idle session expires.

Panel domain & HTTPS

Out of the box the panel is served over plain HTTP on http://IP:8090. To put it on its own domain with a certificate:

  1. Point DNS

    Create an A record, e.g. panel.example.com → your droplet IP.

  2. Settings → Panel domain

    Enter the domain and keep "Issue SSL certificate" on. The panel writes a host nginx vhost on SM_PANEL_DOMAIN_PORT (default 2509) that reverse-proxies to 127.0.0.1:8090, opens that port in UFW and requests a certificate. Port 80 only answers Let's Encrypt challenges for the domain (it must stay open for issuing and renewing), and port 443 refuses the connection, so https://panel.example.com without the port loads nothing.

  3. Lock it down

    Once https://panel.example.com:2509 works, edit .env:

    SM_BIND=127.0.0.1
    SESSION_SECURE_COOKIE=true
    APP_URL=https://panel.example.com:2509

    Then apply it and close the old raw port in UFW:

    $ docker compose up -d
    $ ufw delete allow 8090/tcp

    Finally, allow the panel port only from your own IPs in your network firewall (e.g. a DigitalOcean Cloud Firewall).

Only bind to 127.0.0.1 after you've confirmed the domain loads. If you lock yourself out, SSH in, set SM_BIND=0.0.0.0 and run docker compose up -d.

Changing the panel domain port

Use the script rather than editing .env by hand. It updates SM_PANEL_DOMAIN_PORT (and APP_URL if it uses the old port), recreates the container, rewrites the panel's nginx vhost, opens the new port in UFW and closes the old one. If nginx rejects the new port, everything is put back.

$ cd /opt/server-manager
$ sudo bash scripts/panel-port.sh 2509

Run it without a port to re-apply the one already in .env, e.g. after editing it by hand. Afterwards, open the new port in your network firewall and close the old one.