View Categories

Manual Installation

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.

Terminal output from the database seeder showing Owner User Created with the seeded e-mail address and a generated password
The seeder prints the Super Admin credentials once; copy them before you clear the terminal.

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:cache or config:cache on 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 with Call to a member function bindTo() on null. Config caching would freeze the licence key, which is read from .env at 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.