- Before you start
- 1. Put the files in place
- 2. Install the PHP dependencies
- 3. Build the front end
- 4. Create the database
- 5. Write the environment file
- 6. Migrate and seed
- 7. Link storage and cache the views
- 8. Set ownership and permissions
- 9. Configure the web server
- 10. Start the queue worker and the scheduler
- Troubleshooting
Use a manual installation when the automated installer cannot be used — an existing server with its own nginx or Apache configuration, a container image, or a distribution the installer does not recognise. Every step below mirrors what install.sh does, so the result is the same deployment, just assembled by hand.
Before you start #
- PHP 8.2 or newer with the extensions listed in System requirements, and the ionCube Loader active for both the FPM and CLI SAPIs. Nothing will run without it — the shipped build is encoded.
- Composer 2, Node.js 22.x with npm, MySQL or MariaDB, and Redis.
- The RackHelm release archive and your licence key.
Warning: Verify the Loader before anything else with
php -v— the output must mention ionCube. If it does not, every Composer and Artisan command below fails with a cryptic parse error.
1. Put the files in place #
Extract the archive to the directory the web server will serve from. The installer uses /var/www/rackhelm and the rest of this article assumes that path.
mkdir -p /var/www/rackhelm
tar -xzf rackhelm-release.tar.gz -C /var/www/rackhelm --strip-components=1
cd /var/www/rackhelm
If you are reusing a directory that previously held RackHelm, delete the compiled caches before you continue — a manifest generated while development dependencies were present will fatal on boot under a --no-dev install:
rm -f bootstrap/cache/packages.php bootstrap/cache/services.php bootstrap/cache/config.php
2. Install the PHP dependencies #
composer install --no-dev --optimize-autoloader
--no-dev matters: the development packages are not shipped or needed in production, and installing them changes the discovered provider list.
3. Build the front end #
npm install
npm run build
npm run build runs Vite and writes the compiled CSS and JavaScript into public/build. Without it every page loads unstyled and the admin area’s interactive components do not work.
4. Create the database #
Create an empty database and a user with full privileges on it:
CREATE DATABASE `rackhelm`;
CREATE USER 'rackhelm_user'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON `rackhelm`.* TO 'rackhelm_user'@'localhost';
FLUSH PRIVILEGES;
5. Write the environment file #
cp .env.example .env
php artisan key:generate
key:generate needs .env to be writable by the user running it. If you would rather not grant that, generate the key yourself and paste it in:
echo "APP_KEY=base64:$(openssl rand -base64 32)"
Then edit .env. Check each key below: edit the line if it is already there, and append it if it is not — several of these are not in .env.example at all. Where a key ends up defined twice, the first occurrence wins, so delete duplicates rather than adding another line at the bottom.
APP_ENV=production
APP_DEBUG=false
APP_URL=https://portal.example.com
ASSET_URL=https://portal.example.com
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=rackhelm
DB_USERNAME=rackhelm_user
DB_PASSWORD=a-long-random-password
CACHE_STORE=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
REDIS_PREFIX=rackhelm-database-
HORIZON_PREFIX=rackhelm_horizon:
RACKHELM_LICENSE_KEY="your-licence-key"
Every key is explained in The .env file explained. Two are easy to miss: APP_URL must be the exact public URL, because RackHelm only trusts requests whose Host matches it; and the two Redis prefixes should be pinned literally rather than left to derive from APP_NAME.
6. Migrate and seed #
php artisan migrate --seed --force
The seeder creates roles, the Super Admin account, countries, a support department, company settings, the shipped content pages, the system schedule rows, the report definitions and the site templates. Watch the output: when the Super Admin is created for the first time it prints Owner User Created! followed by Email: and Password: lines. The address is the fixed seed value admin@orbi-hosting.com; the password is 16 random characters and is never displayed again. Change both as soon as you sign in.

Tip: If you only want the schema and not the seed data, run
php artisan migrate --force. You will then have no Super Admin account and no seeded settings, so this is only sensible when restoring an existing database.
7. Link storage and cache the views #
php artisan storage:link
php artisan view:cache
storage:link creates the public/storage symlink that serves uploaded logos and page assets.
Warning: Do not run
php artisan optimize,route:cacheorconfig:cacheon an ionCube-encoded build. Route caching serialises closure-based routes and closure serialisation reads PHP source that the encoder removes, so the cached routes fail at runtime withCall to a member function bindTo() on null. Config caching would freeze the licence key, which is read from.envat runtime. Only view caching is safe.
8. Set ownership and permissions #
RackHelm’s security model keeps the application source read-only to the PHP runtime user, so a file-write bug in the application cannot rewrite public/index.php or a Blade template. Replicate it (substitute nginx for www-data on RHEL):
chown -R root:www-data /var/www/rackhelm
chmod -R u=rwX,g=rX,o= /var/www/rackhelm
chown -R www-data:www-data storage bootstrap/cache
chmod -R u=rwX,g=rX,o= storage bootstrap/cache
chown root:www-data .env
chmod 640 .env
9. Configure the web server #
Point the document root at /var/www/rackhelm/public and send everything that is not a real file to index.php. A ready-made nginx server block, including the security headers and the PHP-FPM guard the installer applies, is in Web server, SSL and HTTPS. For Apache, enable mod_rewrite and allow .htaccess overrides on the directory — RackHelm ships a complete public/.htaccess with the rewrite rules and security headers already in it.
Install a TLS certificate before you try to log in. The session cookie is HTTPS-only, so a plain-HTTP portal rejects every login attempt with a 419 error.
10. Start the queue worker and the scheduler #
Nothing in RackHelm provisions, invoices or e-mails without a running queue worker. Create a Supervisor program for Horizon and a one-line cron entry for the scheduler, both covered step by step in Queue workers, Horizon and the scheduler.
Troubleshooting #
| Symptom | Cause and fix |
|---|---|
| Parse errors or blank pages immediately after install | The ionCube Loader is not active. Check php -v for both the CLI and the FPM SAPI — php -i | grep ioncube under FPM is not the same check as the CLI one. |
Class LaravelPailPailServiceProvider not found |
A stale bootstrap/cache/packages.php from a build that had development dependencies. Delete the three files in step 1 and re-run composer install --no-dev. |
400 Bad Request on every page |
APP_URL does not match the hostname you are visiting. RackHelm trusts only the host in APP_URL; fix it and clear nothing else — the value is read at runtime. |
Login returns 419 Page Expired |
No TLS. The session cookie is marked Secure, so the browser never stores it over plain HTTP. |
| Pages load without styling | npm run build was not run, or public/build is not readable by the web-server user. |
Licence validation error: No RackHelm licence key configured. |
RACKHELM_LICENSE_KEY is empty or missing from .env. See Licensing and activation. |
| Uploaded logos and page images 404 | php artisan storage:link was not run, or the symlink was lost when the tree was re-copied. |