Hosting platforms

Installing RKHM on Ubuntu 24.04 LTS

A clean VPS to a running platform, from the command line

This guide takes an existing Ubuntu 24.04 LTS server to a working RKHM installation with Apache, PHP 8.3, and a local MariaDB database. It gives the Ubuntu commands that matter to RKHM; it does not cover creating a VPS, managing SSH keys, or operating a Linux server generally. Use it alongside RKHM's standard installation steps. The sample commands use music.example.com and /var/www/rkhm; Section 1 explains how to use the main domain example.com instead.

Ubuntu 24.04 LTS is used here because its packaged PHP version is 8.3 and it remains within Ubuntu's standard support window. A newer Ubuntu release may ship a newer PHP version; RKHM's installer checks only a minimum version and required extensions, so that check alone does not prove compatibility with every newer PHP release. See Ubuntu's release cycle and 24.04 PHP packages.

Before you start

  • Have a VPS or dedicated server with Ubuntu 24.04 LTS, a sudo-capable login, the complete RKHM release package and purchase code, a registered domain, and a Cloudflare account. The domain can remain registered with your current registrar while Cloudflare manages its DNS. This is a single-server example: Apache, PHP, the RKHM files, and MariaDB all live on that server.
  • Choose the final public hostname before activation. RKHM's licence terms bind a purchase code to one domain/IP address; a move to another address may need a reset. In the installer, use the final https:// URL, with the trailing slash.
  • Keep the domain reachable on ports 80 and 443. If your provider has a cloud firewall as well as Ubuntu's firewall, both must permit web traffic. Keep SSH access available while changing firewall rules. Ubuntu's firewall guide covers UFW in more depth.

1. Set up the domain in Cloudflare DNS

Use Cloudflare as the domain's authoritative DNS provider. Add the domain (example.com) to Cloudflare and choose a plan. Decide whether RKHM will use the main domain (example.com) or a subdomain (music.example.com); the DNS name, Apache hostname, certificate, and RKHM licence URL must all match that choice. Cloudflare assigns two nameservers; the domain can remain with its present registrar. Follow Cloudflare's current primary DNS setup guide. Prepare the DNS records before changing nameservers so the domain and its mail keep working during the switch.

  1. In Cloudflare, review the imported DNS records and add anything its scan missed. Preserve existing apex, www, MX, SPF, DKIM, DMARC, and verification records that your domain actually uses. Cloudflare warns that its quick scan can miss records; compare with the old DNS provider before switching. See Review your DNS records.
  2. Go to DNS → Records → Add record. For RKHM on the main domain, create or edit an A record with Name @ and IPv4 address set to the Ubuntu server's public IPv4 address. For RKHM on music.example.com, use Name music instead. Cloudflare may already have imported an apex A record; edit that record if it points elsewhere instead of leaving a conflicting old address. Add an AAAA record for the same name only if the server has working public IPv6. Set the chosen RKHM web record to DNS only (gray cloud) during installation and leave TTL at Auto. See Cloudflare's zone apex instructions, subdomain instructions, and proxy status explanation.
  3. If DNSSEC is active at the current registrar, follow Cloudflare's instructions to remove the old DS record before changing nameservers. At the registrar, replace the existing authoritative nameservers with the exact two assigned by Cloudflare. If the domain is registered with Cloudflare Registrar, it already uses Cloudflare nameservers. Wait until Cloudflare shows the zone as Active. From then on, make this domain's A/AAAA changes in Cloudflare, rather than at the old DNS provider.

Current Cloudflare DNS Records page showing Add record and separate A records for the main domain and subdomains
Cloudflare's DNS Records page: choose Add record or edit an existing A record. Its sample addresses and proxy statuses are illustrative; set your RKHM record to DNS only for installation. Screenshot: Cloudflare DNS changelog, 20 May 2026.

Cloudflare DNS setup notice asking for a root-domain record and showing the Add record button
Cloudflare calls out the root domain as a separate record from www. This is an earlier dashboard layout; use the current Records page shown above. Screenshot: Cloudflare Blog.

For the main domain, enter @ only in Cloudflare's Name field. Cloudflare uses it for the zone apex, so an A record named @ in the example.com zone makes example.com resolve to your server. After saving, the record list may display example.com instead of @. @ is not part of a web address, Apache ServerName, certificate command, or RKHM licence URL. Cloudflare's official apex example uses exactly this name.

Type: A
Name: @
IPv4 address: YOUR_SERVER_PUBLIC_IP
Proxy status: DNS only
TTL: Auto

If you choose the main domain, substitute example.com for every later music.example.com example in this guide, including Apache's ServerName, the Certbot -d value, the installer and licence URLs, and the Server step's self-check URL. If you want www.example.com too, add a separate CNAME record with Name www and Target example.com (or an A record pointing to the same public IP) and configure Apache and the TLS certificate for that hostname as well; the @ record alone does not cover www.

While the RKHM record is DNS only, confirm your chosen hostname (example.com for @, or music.example.com for music) resolves to the server's public IP from another network and from the Ubuntu server. Run the corresponding command below. The hostname must also connect from that server: RKHM later requests its own /api/client_config URL to verify Apache rewriting. If it does not resolve, check the assigned nameservers, zone status, and old DNSSEC DS record before continuing.

# Main domain (@):
getent ahostsv4 example.com

# Subdomain (music):
getent ahostsv4 music.example.com

If UFW is in use, allow SSH and HTTP/HTTPS before enabling it. The exact SSH profile may differ if you changed the SSH port; confirm access in another session. Ubuntu documents UFW commands and application profiles. Your provider's network firewall needs equivalent access.

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw status

2. Install the web stack RKHM needs

Install Ubuntu's Apache module for PHP 8.3, the CLI interpreter for RKHM's scheduled job, MariaDB, cron, and the PHP extensions the RKHM installer checks. The MySQL package supplies MySQLi for MariaDB connections; XML supports RKHM's bundled PHP libraries. This example uses Ubuntu packages rather than an additional PHP repository; see Ubuntu's Apache and PHP guides and MariaDB's installation guide.

sudo apt update
sudo apt install apache2 mariadb-server cron \
  libapache2-mod-php8.3 php8.3-cli php8.3-curl php8.3-mysql \
  php8.3-mbstring php8.3-gd php8.3-zip php8.3-xml
sudo systemctl enable --now apache2 mariadb cron
sudo a2enmod rewrite headers access_compat
sudo systemctl restart apache2

The rewrite and headers modules are needed by RKHM's unchanged root .htaccess. Some protected RKHM directories also contain Apache's older Order/Deny directives, so access_compat is needed for those files on Apache 2.4. Apache documents the compatibility module; Ubuntu documents a2enmod.

Check that the CLI version is 8.3 and contains the extensions. The browser installer checks the Apache PHP configuration separately. If the Requirements step reports allow_url_fopen disabled, enable it in the active Apache PHP configuration (normally /etc/php/8.3/apache2/php.ini), restart Apache, and refresh the installer. For the cron interpreter, use php8.3 --ini to locate its separate CLI configuration.

php8.3 -v
php8.3 -m
sudo systemctl status apache2 mariadb cron --no-pager

3. Put the RKHM release in the document root

Create the site directory, then copy the contents of the release package's script directory into it. Use your actual unpacked release path in the second command. cp -a .../. includes hidden files, including .htaccess. The result should contain /var/www/rkhm/index.php, /var/www/rkhm/api/, /var/www/rkhm/admin/, /var/www/rkhm/installer/, and /var/www/rkhm/.htaccess directly under the site root. An extra script/ level changes all URLs. Use the complete release package, not a development checkout with symlinks to directories outside it.

sudo mkdir -p /var/www/rkhm
sudo cp -a /path/to/unpacked-release/script/. /var/www/rkhm/
ls -la /var/www/rkhm

Keep the original .htaccess files byte-for-byte: RKHM's installer checks hashes for the root, API, admin, and framework rules. Do not paste rewrite rules from a generic PHP tutorial into them. Configure Apache around the package instead.

Apache's PHP process runs as www-data in this example. It needs read access to the application, permanent write access to files/ for uploaded and generated media, and temporary write access to api/app/ during installation so it can create or update config_user.php and config_license.php. The commands below assume you are deploying from a normal sudo-capable account. If either config file already exists, grant group write on that file for the installer too. Do not make the whole site world-writable.

sudo chown -R "$USER":www-data /var/www/rkhm
sudo chmod -R g+rwX /var/www/rkhm/files
sudo chmod g+w /var/www/rkhm/api/app
for file in /var/www/rkhm/api/app/config_user.php /var/www/rkhm/api/app/config_license.php; do
  if [ -f "$file" ]; then sudo chmod g+w "$file"; fi
done

4. Give Apache a site for that hostname

Save this example as /etc/apache2/sites-available/rkhm.conf, using ServerName example.com if you chose the Cloudflare @ record (or replacing the hostname and path if necessary). Its DocumentRoot points at RKHM's index.php, not at api/ or the outer ZIP folder. AllowOverride All lets Apache read the packaged .htaccess files; FollowSymLinks supports their rewrite rules. Ubuntu's VirtualHost guide explains site files and a2ensite; Apache documents AllowOverride.

<VirtualHost *:80>
    ServerName music.example.com
    DocumentRoot /var/www/rkhm

    <Directory /var/www/rkhm>
        Options FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/rkhm-error.log
    CustomLog ${APACHE_LOG_DIR}/rkhm-access.log combined
</VirtualHost>

Enable the site and check Apache's syntax. If the default Ubuntu site is still enabled, you can disable it once your RKHM host responds correctly; it is not necessary to break other sites on a shared server. Open http://music.example.com/installer/ from outside and confirm this server answers before requesting a certificate.

sudo a2ensite rkhm.conf
sudo apache2ctl configtest
sudo systemctl reload apache2

5. Turn on HTTPS before activating the licence

Keep the chosen @ or music record DNS only while setting up the origin certificate, so the hostname reaches Apache directly. Use the official Certbot Apache instructions to install Certbot, issue a certificate for your chosen hostname, and configure HTTPS. Certbot's Apache method expects the HTTP site to be reachable on port 80. After Certbot is installed, its Apache command can select the site and install the certificate; follow its prompts, then check renewal as described on that page. Certbot may edit the Apache VirtualHost configuration; leave RKHM's packaged .htaccess files untouched.

sudo certbot --apache -d music.example.com
sudo certbot renew --dry-run

For the main domain, use sudo certbot --apache -d example.com instead. If you also serve www.example.com, first create its DNS record and Apache ServerAlias, then request both names with sudo certbot --apache -d example.com -d www.example.com.

Visit the HTTPS installer on your chosen hostname (https://example.com/installer/ for @, or https://music.example.com/installer/ for music) and confirm a valid certificate. At RKHM's licence step use the matching full website address with a trailing slash, such as https://example.com/ for the main domain. The installer saves that address, and its later route check requests the same address from the server. If a proxy, DNS setting, firewall, or TLS problem prevents the server from reaching itself, solve that before continuing. HTTPS also matters for browser features and external callbacks used after installation.

Cloudflare can remain the DNS provider while this record stays DNS only. If you later switch the chosen @ or music record to Proxied (orange cloud), select SSL/TLS → Overview → Full (strict) in Cloudflare after the origin has its valid Certbot certificate. Full (strict) validates the certificate between Cloudflare and Apache; Cloudflare's visitor-facing certificate alone does not secure that origin connection. Avoid Flexible mode with Apache's HTTPS redirect, which Cloudflare documents as a cause of redirect loops. See Cloudflare's Full (strict) requirements and redirect-loop guidance. Recheck RKHM's installer and API route after changing proxy status.

6. Create an empty MariaDB database

Connect as MariaDB's local administrator with sudo mariadb. Create an empty database and a dedicated local account; replace the example password with a strong unique value. Grant rights only on the RKHM database so the installer can create its tables and the application can manage them later. You do not need to import RKHM's SQL manually—the installer handles that. MariaDB documents CREATE DATABASE, CREATE USER, and database-scoped GRANT.

sudo mariadb
CREATE DATABASE rkhm CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'rkhm'@'localhost' IDENTIFIED BY 'replace-with-a-strong-password';
GRANT ALL PRIVILEGES ON rkhm.* TO 'rkhm'@'localhost';
EXIT;

At RKHM's Database step, use host localhost, database rkhm, user rkhm, and that password. Enter a separate admin email and password. The installer imports its schema into the empty database. If an import fails partway through, follow the installer's incomplete-database message and clear the partial tables before retrying; do not activate a partly imported database.

7. Complete RKHM's installer in order

Open the HTTPS installer and follow Requirements, Licence, and Database in that order. The requirements screen checks PHP's cURL, MySQLi, mbstring, GD, ZIP, allow_url_fopen, and PHP version. The licence screen calls Busyowl's service and writes the licensed URL. The database screen imports tables and creates the RKHM admin account. If the first screen flags an extension, install/enable it for Apache's PHP version and restart Apache; php8.3 -m only proves what the CLI sees.

Before the Server step can pass, schedule RKHM's own CLI command once per minute. It must run after the database exists. Ubuntu's cron service and the www-data account are already present in this example. Edit that account's crontab and add exactly one entry; use the absolute PHP 8.3 binary so a later change to the system's default php command cannot silently change RKHM's cron version.

sudo crontab -u www-data -e
* * * * * /usr/bin/php8.3 -f /var/www/rkhm/api/admin.php bof_cronjobs=yes

Save the crontab, wait a minute or two, then refresh RKHM's Server step. Its cron check reads the heartbeat written to the database; it also checks the original .htaccess files, makes a live request to the chosen hostname's /api/client_config URL (for example, https://example.com/api/client_config when using @), and checks the database/PHP timezone. If the heartbeat stays red, run the same command manually as the scheduled account to see the error, then check systemctl status cron and the PHP CLI configuration. Keep only one scheduler entry for this installation.

sudo -u www-data /usr/bin/php8.3 -f /var/www/rkhm/api/admin.php bof_cronjobs=yes

8. Finish the site, uploads, and media tools

When the installer finishes, remove the installer/ directory and continue with First run. Remove the temporary web-server write permission from api/app/ and return the generated config files to your deployment account while retaining www-data read access. Keep files/ writable for media. If you later install plugins or themes through RKHM, those directories may need write access for that operation.

sudo rm -rf /var/www/rkhm/installer
sudo chown "$USER":www-data /var/www/rkhm/api/app/config_user.php /var/www/rkhm/api/app/config_license.php
sudo chmod 640 /var/www/rkhm/api/app/config_user.php /var/www/rkhm/api/app/config_license.php
sudo chmod g-w /var/www/rkhm/api/app

For uploads larger than PHP's defaults, set upload_max_filesize and a slightly larger post_max_size in Apache's active /etc/php/8.3/apache2/php.ini, then restart Apache. Set limits for the largest files your users may upload and the server's available resources. If RKHM cannot write an upload, inspect /var/www/rkhm/files/ permissions and disk space before changing the whole site's permissions.

Cloudflare's proxy has its own upload and connection limits in addition to PHP's. Cloudflare currently lists a 100 MB maximum upload on Free and Pro plans and documents a proxy read timeout; either can matter for RKHM media. If you need larger uploads, leave this RKHM hostname DNS only or choose a Cloudflare plan and upload design that supports the real file sizes. Test an actual upload and playback before enabling the orange-cloud proxy. See Cloudflare's upload limit and connection limits.

FFmpeg and other extra services can be added after RKHM's core installation. The Optional tools on Ubuntu section below gives the Ubuntu checks and exact RKHM Admin controls.

Back up the MariaDB database and /var/www/rkhm/files/, plus the generated config files and any custom plugins/themes. Confirm a restore procedure before relying on backups. This guide does not choose a backup provider or retention policy; Ubuntu's backup documentation covers one system-level option.

If something stays red

  • Requirements: check the Apache PHP module version and active php.ini; install the missing php8.3-* package, restart Apache, and refresh the page. The CLI module list can differ from Apache's.
  • Cloudflare DNS, licence or HTTPS: confirm the Cloudflare zone is Active and the chosen @ or music A record points to this server. If DNS only, the hostname should resolve to the origin IP. If proxied, check Cloudflare Full (strict), the origin certificate, and the request path from the server to its own public URL. RKHM ties the purchase code to the submitted host and must reach the same URL for its route check.
  • Database: confirm MariaDB is running, the empty rkhm database exists, and the 'rkhm'@'localhost' account has privileges on rkhm.*. An incomplete import must be cleared before retrying.
  • .htaccess: restore any missing or edited file from the original release. If the files pass but API routes fail, check the correct VirtualHost, AllowOverride All, rewrite, headers, access_compat, and Apache's site error log at /var/log/apache2/rkhm-error.log.
  • Cron: check that the job runs as www-data with /usr/bin/php8.3, the script path is correct, and MariaDB is available. Run the command manually as shown above to see the error.

Optional tools on Ubuntu

These tools are optional for the basic RKHM installer. Add only the features your site will use. The paths below are examples for a system-wide Ubuntu 24.04 installation; run command -v on your own server and enter the path it returns. A tool that works in your SSH session must also be executable by Apache's www-data account. Keep the once-per-minute RKHM cron job from Step 7 running, because background media and search work depends on it.

1. FFmpeg and FFprobe

FFmpeg handles RKHM's media conversion, lower-quality copies, HLS output, and previews. FFprobe is installed with the same Ubuntu package and lets RKHM inspect video streams before deciding whether to copy or re-encode them. Install the package and check both binary locations. See the RKHM FFmpeg guide and FFmpeg's Linux download page.

sudo apt update
sudo apt install ffmpeg
command -v ffmpeg
command -v ffprobe
sudo -u www-data /usr/bin/ffmpeg -version
sudo -u www-data /usr/bin/ffprobe -version

In Setting → General → External Tools → FFmpeg, put the returned path (normally /usr/bin/ffmpeg) in Absolute/Shell path, use Test, then turn on Enable. Leave Use FFmpeg Static Builds off when using Ubuntu's native binary. Start with the default CPU encoder and encoding priority; select hardware encoding only after its separate test succeeds on this server. In the adjacent FFprobe group, enter /usr/bin/ffprobe, use Test, then turn on its Enable switch. FFprobe must not be enabled before FFmpeg works. Enable any desired conversion, quality, HLS or preview options separately under Admin → Settings → Files → Upload setting; installing FFmpeg alone does not select those policies.

2. Meilisearch

Meilisearch is an optional search service for larger catalogues. Follow Meilisearch's current installation guide and production service guide. Run it as a persistent service with a writable data directory, production mode and a strong master key. For this single-server setup, bind it to 127.0.0.1:7700; RKHM connects server-side, so port 7700 need not be public. Back up its data as well as MariaDB. Meilisearch Cloud is also an option if you do not want to run that service yourself.

In Admin → Settings → General → Search → Meilisearch, set Endpoint Address to http://127.0.0.1:7700 for the local service, and put its master key in Master key. Use Test and confirm the reported version. Then select Meilisearch in the Search Engine control on the same page and save. RKHM's existing cron job builds the indexes; watch Meilisearch - Progress and the search cron logs before judging search results. If you keep the default PHP search engine, no Meilisearch install or key is needed. See RKHM's Search system guide.

3. yt-dlp

Install a current yt-dlp release using the official installation guide; its own project notes that third-party distribution packages can lag behind changes on video sites. Install FFmpeg too if downloads need audio/video merging or conversion. Put yt-dlp somewhere the web and cron account can execute, then check its version and path. Update it by the method used for installation; for the official release binary, the project documents yt-dlp -U.

YT_DLP_BIN=$(command -v yt-dlp)
echo "$YT_DLP_BIN"
sudo -u www-data "$YT_DLP_BIN" --version

In Setting → General → External Tools → youtube-dl, enter the path returned by command -v yt-dlp in youtube-dl local path (for example /usr/local/bin/yt-dlp) and use Test. The group keeps its older youtube-dl label but accepts yt-dlp. Leave the proxy field empty unless your network requires one. If a provider requires authenticated cookies, RKHM's setting explains its optional protected files/protected/yt_cookies.php file; treat exported cookies as credentials. Download only media you are permitted to use. For full YouTube support, see yt-dlp's EJS setup guide: its solver scripts and a supported JavaScript runtime are required. With Node.js 22 or later, enable Node for yt-dlp with --js-runtimes node in the yt-dlp configuration used by www-data; RKHM's separate Node.js path field does not set this yt-dlp option.

4. WebP image requirements

RKHM bundles the Spatie image-optimizer PHP library. Its external Ubuntu binaries include cwebp (from webp) and optimizers for JPEG, PNG and GIF. Install the tools, then check that Apache's www-data account can find them. SVG's svgo is only relevant if your deployment processes SVG; it is not required for ordinary RKHM JPG/PNG uploads. See the Spatie binary list and RKHM's WebP guide.

sudo apt install webp jpegoptim optipng pngquant gifsicle
command -v cwebp jpegoptim optipng pngquant gifsicle
sudo -u www-data /usr/bin/cwebp -version

The control is Setting → General → External Tools → webp → Make webp of all uploaded images. Keep it off: in the current release it doesn't produce real WebP files — the optimizer saves an optimized JPEG or PNG under a .webp name, and installing the binaries doesn't change that. See WebP.

5. Animated GIF uploads

For animated GIFs, RKHM checks whether PHP's Imagick class is available and uses it while sanitizing GIF uploads. On Ubuntu 24.04 with this guide's PHP 8.3, install the matching php8.3-imagick extension and ImageMagick, then restart Apache. Check both CLI loading and the status shown by RKHM's Admin tip; Apache may use a different PHP configuration from the CLI. See Ubuntu's package entry and the PHP Imagick manual.

sudo apt install php8.3-imagick imagemagick
sudo systemctl restart apache2
php8.3 -m | grep -i imagick

In Admin → Settings → Files → Upload setting → Image, check that the Animated GIFs tip says ImageMagick is available before enabling that switch. Upload one small animated GIF and confirm its animation survives and its size fits your image limits. The gifsicle binary in the WebP section is an image optimizer; it does not replace the PHP Imagick extension for RKHM's GIF validation.

6. Node.js

Node.js is optional for RKHM's P0dCr8 and NFT plugins; the core installer does not require it. Install a supported, system-wide LTS release from the official Node.js download page so Apache's www-data account can run it. A per-user version-manager path in your SSH home directory may not be visible to Apache or cron. Check the version and the absolute path on the server.

NODE_BIN=$(command -v node)
echo "$NODE_BIN"
sudo -u www-data "$NODE_BIN" --version

In Setting → General → External Tools → Node.js, put the returned absolute path (commonly /usr/bin/node for a system package) in Node.js local path and use Test. RKHM has no separate Node.js enable switch. If the test reports exec or proc_open disabled, those PHP functions must be available to the Admin test and the plugin's server-side process. Set up the plugin that needs Node.js only after this test succeeds.