Shared host (cPanel / Plesk)
About 831 wordsAbout 3 min
Spora’s original target environment: a shared host with PHP 8.4+, FTP or SSH access, and a public_html-style document root. No Docker, no root, no system services. Just a composer create-project and a couple of chmod calls.
If you have root or VPS access, Classical server(Open in new window) or Docker is a better fit. The cPanel path is for resource-constrained shared hosting where the host is the only thing available.
Prerequisites
- PHP 8.4+ — check via the cPanel “MultiPHP Manager” or
php -vover SSH. Spora usesreadonlyproperties, enums, and the FrankenPHP runtime; PHP 8.3 or earlier will not work. pdo_mysqlextension — even if you use SQLite at runtime, the platform check at install time requires it. Most hosts include it; if not, ask the host to enable it or pick a host that does.- SSH access (preferred) or FTP-only (slower but works)
- A document root that points to the skeleton’s
public/directory. For cPanel, this is usuallypublic_html/. - PHP CLI access — to run
composer installandphp bin/spora spora:install. Some shared hosts restrict CLI; if yours does, ask the host to enable SSH, or use FTP-only (steps below).
Step-by-step install
1. Create the project locally
On your laptop, in a terminal:
composer create-project spora-ai/spora my-spora
cd my-spora
composer install --no-dev --optimize-autoloader # production-only deps2. Generate the secret key
php -r "echo base64_encode(random_bytes(32));" > .secret_keyKeep .secret_key somewhere safe — you’ll paste it into .env next.
3. Edit .env
cp .env.example .env
nano .envFill in at minimum:
SPORA_APP_ENV=production
SPORA_ALLOW_REGISTRATION=true # true for first signup, then false
SPORA_SECRET_KEY=<paste the contents of .secret_key>
SPORA_DB_DRIVER=sqlite # or mysql if your host offers MySQLIf using MySQL, also set:
SPORA_DB_DRIVER=mysql
SPORA_DB_HOST=localhost
SPORA_DB_NAME=<your-cpanel-db-name>
SPORA_DB_USER=<your-cpanel-db-user>
SPORA_DB_PASSWORD=<your-cpanel-db-password>4. Upload to the host
Via SSH (preferred):
# Tar the project (exclude .git, vendor will be re-installed)
tar --exclude='.git' --exclude='storage/*.sqlite' -czf my-spora.tar.gz my-spora
scp my-spora.tar.gz user@host:~/my-spora.tar.gz
ssh user@host
tar -xzf my-spora.tar.gz
cd my-spora
composer install --no-dev --optimize-autoloader
php bin/spora spora:installVia FTP (slower, no CLI):
Upload the entire
my-spora/directory EXCEPTvendor/,node_modules/,.git/,storage/*.sqliteto the host (abovepublic_html/)Open a web terminal (cPanel offers
Terminalin some plans) or ask the host to enable SSHRun:
cd ~/my-spora composer install --no-dev --optimize-autoloader php bin/spora spora:install
5. Configure the document root
Spora’s public/ directory is the web root. The host’s public_html/ (or equivalent) needs to point inside my-spora/public/, not at my-spora/ itself.
cPanel (MultiPHP Manager):
- Click “Set PHP versions per directory” or “Document Root”
- Set the document root for the domain to
my-spora/public - Save
Plesk (Domain Settings):
- Set “Document root” to
my-spora/public - Save
Apache .htaccess (fallback — most cPanels auto-generate this):
RewriteEngine On
RewriteRule ^(.*)$ public/$1 [L]6. Set permissions
chmod -R 775 storage
chmod 644 .envThe storage/ directory needs to be writable by the web server user (often nobody or www-data).
7. First-run signup
Visit https://yourdomain.com. With SPORA_ALLOW_REGISTRATION=true, you’ll see the signup form. Create your admin account.
Immediately after signup, set SPORA_ALLOW_REGISTRATION=false in .env to prevent random signups. Re-upload the updated .env.
8. Verify
curl -I https://yourdomain.com/health
# → 200 OK
curl https://yourdomain.com/api/v1/auth/me
# → 401 (no session) — expectedDaily operation
- Updates: SSH in, run
composer update spora-ai/spora, thenphp bin/spora spora:installto apply any new migrations. - Backups: see Backups. SQLite is one file;
.envandstorage/secret.keyare the other essentials. - Logs:
tail -f storage/spora.log(Monolog) andtail -f storage/php.log(PHP errors). Most cPanels also expose these via the control panel.
Cron workers
If you set SPORA_SYNC_MODE=false to use the async worker:
* * * * * cd /home/user/my-spora && /usr/bin/php bin/spora worker:run --once --include-queue >> storage/spora.log 2>&1Run this every minute. For tasks that exceed 60 s, consider upgrading to Classical server(Open in new window) and running the daemon instead.
Important constraints
See the Limitations page. In short:
- The Mercure SSE hub requires a long-lived PHP process; on most shared hosts, it falls back to polling.
- Worker daemon mode is unreliable on most shared hosts (no
nohup, no systemd). - HTTPS termination is the host’s responsibility (cPanel’s AutoSSL or your DNS provider).
- Custom Docker images are not deployable.
Next steps
- Limitations — what doesn’t work on shared hosts
- Backups — the operator’s backup strategy
- Day-2 ops — plugin management, updates, logs, reset
- Classical server(Open in new window) — when you outgrow shared hosting