KeyAuth
Sign in

Deployment

PHP 8.3, MySQL and a web server whose document root points at public. Node is only needed to build assets, not to run them.

Requirements

ComponentVersion
PHP8.3 or newer, with openssl, mbstring, pdo_mysql, zip, zlib, fileinfo, curl, bcmath
DatabaseMySQL 8 or MariaDB 10.6, over PDO
Web serverApache 2.4 with rewrite, or nginx
Node20.19+ or 22.12+, only to build assets

Install

bash
composer install --no-dev --optimize-autoloader

cp .env.example .env
php artisan key:generate
# edit .env: APP_URL and the database credentials
php artisan migrate --force

php artisan config:cache && php artisan route:cache && php artisan view:cache

chown -R www-data:www-data storage bootstrap/cache
find storage bootstrap/cache -type d -exec chmod 775 {} \;

APP_KEY is not just a session key here

It encrypts your application secrets and variable values in the database. Generate it once, back it up with your database, and never run key:generate again on a live installation. Doing so makes every stored secret and variable permanently unreadable.

Assets

public/build is gitignored, so a git pull will not bring it. Build it and ship the output, which is the recommended route because the server then needs no Node at all:

bash
npm ci && npm run build
rsync -av public/build/ user@server:/var/www/keyauth/public/build/

Or build on the server:

bash
unset NODE_ENV
npm install --include=dev   # not --omit=dev, Vite lives in devDependencies
npm run build

vite: command not found means devDependencies were skipped. Never copy node_modules between operating systems either; @tailwindcss/oxide ships a platform specific native binary. Copying public/build is safe, since it is only static CSS, JS and fonts.

Apache

apache
<VirtualHost *:443>
    ServerName auth.example.com
    DocumentRoot /var/www/keyauth/public

    SSLEngine on
    SSLCertificateFile    /etc/letsencrypt/live/auth.example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/auth.example.com/privkey.pem

    <Directory /var/www/keyauth/public>
        AllowOverride All
        Require all granted
        Options -Indexes -MultiViews +FollowSymLinks
    </Directory>

    <DirectoryMatch "^/var/www/keyauth/(?!public)">
        Require all denied
    </DirectoryMatch>
</VirtualHost>

The document root must be public. Pointing it at the project root serves .env, and with it your APP_KEY and database password, over HTTP. This is the single most common way a Laravel deployment leaks everything.

Scheduled maintenance

bash
* * * * * cd /var/www/keyauth && php artisan schedule:run >> /dev/null 2>&1

This runs keyauth:prune hourly, closing dead sessions and trimming old activity logs and expired blacklist entries. Without it those tables grow forever.

Behind a proxy

Set TRUSTED_PROXIES when something sits in front of PHP. Leaving it empty behind a proxy makes every client IP read as the proxy address, which silently defeats IP blacklists and rate limits. Setting it with no proxy in front lets clients spoof their IP through X-Forwarded-For. Both directions are wrong, so match it to your actual topology.

First account

Registration is closed by default, but the very first account is always allowed and becomes an administrator. Create it at /register, then keep KEYAUTH_REGISTRATION_OPEN=false.