Shared host (cPanel / Plesk)
About 1098 wordsAbout 4 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/api/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). Most cPanels also expose this via the control panel.
Choose how tasks run
You have two real paths on a shared host:
- Server-side worker — a long-lived PHP process drains the queue (Docker, VPS, classical server with supervisord/systemd). Scheduled runs dispatch on time. Automation runs while you’re offline. See Classical server(Open in new window).
- Browser-driven worker (client-worker mode) — your browser ticks the tasks. Set
SPORA_WORKER_RUNTIME_MODE=clientin.envand leave a browser tab open. Tasks dispatch while you’re logged in. See Client-worker mode.
If you have shell access to run a daemon, take that path — it’s the better default. Client-worker mode is the workaround for hosts that genuinely cannot run a long-lived PHP process.
Why not cron? Most shared-host cron systems cap the execution duration (typically 30–60 seconds) and the number of entries. A long LLM response loop will be killed mid-step, leaving a half-finished orchestrator state that the reaper then flags as orphaned. If your host’s cron allows long-running scripts (rare on shared hosting), php bin/spora worker:run --once --include-queue is technically available — but for any realistic LLM workload, pick one of the two paths above.
Browser-driven worker (recommended for shared hosts)
With SPORA_WORKER_RUNTIME_MODE=client set in .env, the browser drives the agent loop via a SharedWorker — no daemon required. Install composer create-project spora-ai/spora my-spora, point the document root at public/, set the env var, open the URL, log in, click a chat. That’s it.
See Client-worker mode for the full guide: browser support, lease semantics, per-runner scoping, scheduled runs, and the manual test scenarios. The trade-off: scheduled runs only dispatch while a browser is open with a logged-in session.
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). Use the browser-driven client-worker mode instead. - HTTPS termination is the host’s responsibility (cPanel’s AutoSSL or your DNS provider).
- Custom Docker images are not deployable.
Next steps
- Client-worker mode — the recommended browser-driven setup
- 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