Forum Platform
    PHP / Laravel

    Deploy Flarum on a VPS

    Self-host Flarum, a lightweight PHP forum platform, on a RamNode KVM VPS with nginx, PHP-FPM, MariaDB, Let's Encrypt, external SMTP, and a working upgrade path.

    Flarum is the lightest of the three mainstream open-source forum platforms. It is a PHP application built on Laravel components with a Mithril frontend, it uses Composer for both installation and extension management, and it runs comfortably on a 1 GB VPS alongside other services. If you have deployed any modern PHP app behind nginx and PHP-FPM, none of this will surprise you.

    This guide covers a production Flarum install on a RamNode KVM VPS with nginx, PHP-FPM, MariaDB, Let's Encrypt, an external SMTP relay, and a working backup and upgrade process.


    1. Version decision: 1.8 or 2.0

    Read this before you type anything, because it determines your PHP version.

    Flarum 2.0 has been in development for roughly three years and is currently in its release-candidate series (rc.4 as of mid-2026). The Flarum team describes the API as stable and confirms that real forums are running 2.0 in production, but it has not shipped a final stable tag. Flarum 2.0 requires PHP 8.3 or higher.

    Flarum 1.8.x is the last stable release line. It runs on PHP 8.2 and 8.3.

    Flarum 1.8.xFlarum 2.0 RC
    StabilityFinal stableRelease candidate
    PHP8.2 or 8.38.3+
    Extension ecosystemCompleteMost major extensions ported, some gaps
    Upgrade pathWill need a 2.0 migration eventuallyAlready there

    Recommendation: if this is a forum you are handing to a client or a community that will be annoyed by breakage, install 1.8.x. If it is your own project and you would rather do the 2.0 migration now instead of later, install the RC. Ubuntu 24.04 ships PHP 8.3 natively, so either choice works without a third-party PPA.

    Note one 2.0 gotcha up front: Flarum 2.0 treats MySQL and MariaDB as distinct drivers. If you use MariaDB, your config.php driver value must be mariadb, not mysql. Setting it wrong produces a scattered set of query failures that look like anything except a driver mismatch. 2.0 rc.3 added detection for this, but know it exists.

    2. Choose the right RamNode instance

    Flarum is genuinely cheap to run.

    Community sizeRAMvCPUDisk
    Small forum, under 100 active users1 GB120 GB
    Under 500 active users2 GB240 GB
    Larger, or with Redis and queue workers4 GB2 to 460 GB+

    Pick Ubuntu 24.04 LTS x86_64 on a KVM plan. Flarum will happily share a 2 GB box with other small PHP sites, which is the main reason to pick it over Discourse.

    3. Prerequisites

    1. A domain or subdomain with an A record on your RamNode IPv4 (and AAAA if you are using IPv6). Propagate it before you request certificates.
    2. An external SMTP relay. Same constraint as every other app you host here.
    3. SSH access as a sudo user.

    The SMTP situation on RamNode

    RamNode does not permit mail services on its VPS platform and outbound port 25 is not available to you. Flarum needs email for registration confirmation, password resets, and notification digests. Configure it against a transactional provider over port 587 or 465.

    Options: Postmark, SendGrid, Mailgun, Amazon SES, Brevo, Scaleway TEM.

    Unlike Discourse, Flarum will install and let you click around without working mail. That is a trap. Registration will break for every user who is not you, and you will find out about it from an angry Discord message. Configure SMTP in the admin panel before you announce the forum, and set SPF, DKIM, and DMARC at your DNS provider.

    4. Base server preparation

    shell
    apt update && apt upgrade -y
    hostnamectl set-hostname forum.example.com
    timedatectl set-timezone UTC

    Reboot if the kernel changed.

    Create a deploy user if you are still on root:

    shell
    adduser deploy
    usermod -aG sudo deploy
    rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy

    Swap

    On a 1 GB plan, composer create-project will pull hundreds of packages and can spike hard. Add swap.

    shell
    fallocate -l 1G /swapfile
    chmod 600 /swapfile
    mkswap /swapfile
    swapon /swapfile
    echo '/swapfile none swap sw 0 0' >> /etc/fstab
    echo 'vm.swappiness=10' >> /etc/sysctl.d/99-flarum.conf
    sysctl --system

    Firewall

    shell
    apt install -y ufw
    ufw allow 22/tcp
    ufw allow 80/tcp
    ufw allow 443/tcp
    ufw --force enable

    Automatic security updates and fail2ban

    shell
    apt install -y unattended-upgrades fail2ban
    dpkg-reconfigure -plow unattended-upgrades
    systemctl enable --now fail2ban

    5. Install the stack

    nginx, PHP 8.3, MariaDB

    Ubuntu 24.04's default PHP is 8.3, which satisfies both Flarum 1.8 and 2.0. No PPA needed.

    shell
    apt install -y nginx mariadb-server \
      php8.3-fpm php8.3-cli php8.3-common \
      php8.3-curl php8.3-dom php8.3-gd php8.3-mbstring \
      php8.3-mysql php8.3-tokenizer php8.3-zip php8.3-xml \
      php8.3-intl php8.3-opcache \
      git unzip curl

    The extension list maps directly to Flarum's requirements: curl, dom, fileinfo, gd, json, mbstring, openssl, pdo_mysql, tokenizer, zip, session. fileinfo, json, openssl, and session are compiled into the Ubuntu PHP build. Verify:

    shell
    php -m | grep -E '^(curl|dom|fileinfo|gd|json|mbstring|openssl|pdo_mysql|tokenizer|zip|session)#x27;
    php -v

    Composer 2

    shell
    curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php
    php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer
    composer --version

    Confirm it reports 2.x. Flarum requires Composer 2 and will not work with 1.x.

    Secure MariaDB

    shell
    mariadb-secure-installation

    Answer: no unix_socket change if you want a root password, set a root password, remove anonymous users, disallow remote root, remove test database, reload privileges.

    Create the database and user:

    shell
    mariadb -u root -p
    shell
    CREATE DATABASE flarum CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
    CREATE USER 'flarum'@'localhost' IDENTIFIED BY 'use-a-long-random-password-here';
    GRANT ALL PRIVILEGES ON flarum.* TO 'flarum'@'localhost';
    FLUSH PRIVILEGES;
    EXIT;

    Confirm MariaDB only listens locally. In /etc/mysql/mariadb.conf.d/50-server.cnf:

    shell
    bind-address = 127.0.0.1
    shell
    systemctl restart mariadb
    ss -lntp | grep 3306

    6. Install Flarum

    Create the install directory and hand it to the deploy user.

    shell
    mkdir -p /var/www/flarum
    chown -R deploy:www-data /var/www/flarum
    cd /var/www/flarum

    As the deploy user (not root, and not www-data):

    shell
    su - deploy
    cd /var/www/flarum

    For Flarum 1.8.x stable

    shell
    composer create-project flarum/flarum:^1.8 .

    For Flarum 2.0 RC

    shell
    composer create-project flarum/flarum:^2.0.0 --stability=RC .

    If you need a third-party extension that only publishes beta releases against 2.0, use --stability=beta instead. Once 2.0 goes stable, set "minimum-stability": "stable" in your composer.json so Composer stops pulling pre-release packages.

    The install pulls a lot of packages. On a 1 GB plan this is where swap matters.

    Permissions

    The web server user needs write access to three places: the root install directory (for config.php), storage/, and public/assets/.

    shell
    sudo chown -R deploy:www-data /var/www/flarum
    sudo find /var/www/flarum -type d -exec chmod 2775 {} \;
    sudo find /var/www/flarum -type f -exec chmod 0664 {} \;
    sudo chmod -R 775 /var/www/flarum/storage /var/www/flarum/public/assets

    Never use 777. If you hit a permissions warning during install, the fix is ownership, not a wider mode.

    Add your deploy user to the web group so you can run composer and php flarum without sudo:

    shell
    sudo usermod -aG www-data deploy

    Log out and back in for the group change to apply.

    7. nginx configuration

    The webroot is public/, not the install root. This is the single most common Flarum misconfiguration and it will expose config.php if you get it wrong.

    Flarum ships a .nginx.conf file in the install root. Include it rather than copying its contents, so upstream changes flow through on upgrade.

    /etc/nginx/sites-available/flarum:

    shell
    server {
        listen 80;
        listen [::]:80;
        server_name forum.example.com;
    
        root /var/www/flarum/public;
        index index.php;
    
        client_max_body_size 32M;
    
        include /var/www/flarum/.nginx.conf;
    
        location ~ \.php$ {
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/run/php/php8.3-fpm.sock;
            fastcgi_index index.php;
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
            fastcgi_param DOCUMENT_ROOT $realpath_root;
            fastcgi_read_timeout 120;
        }
    
        access_log /var/log/nginx/flarum.access.log;
        error_log  /var/log/nginx/flarum.error.log;
    }

    Enable it:

    shell
    ln -s /etc/nginx/sites-available/flarum /etc/nginx/sites-enabled/
    rm -f /etc/nginx/sites-enabled/default
    nginx -t
    systemctl reload nginx

    PHP-FPM pool tuning

    On a 1 GB plan, the default pm.max_children will let PHP-FPM cheerfully OOM the box under load. Edit /etc/php/8.3/fpm/pool.d/www.conf:

    shell
    pm = dynamic
    pm.max_children = 8
    pm.start_servers = 2
    pm.min_spare_servers = 2
    pm.max_spare_servers = 4
    pm.max_requests = 500

    Rule of thumb: a Flarum PHP-FPM child uses 40 to 60 MB. Multiply your max_children by 60 MB and make sure the result leaves headroom for MariaDB and nginx.

    Bump PHP limits in /etc/php/8.3/fpm/php.ini:

    shell
    memory_limit = 256M
    upload_max_filesize = 32M
    post_max_size = 32M
    max_execution_time = 60
    opcache.enable = 1
    opcache.memory_consumption = 128
    opcache.max_accelerated_files = 10000
    opcache.validate_timestamps = 1
    opcache.revalidate_freq = 2
    shell
    systemctl restart php8.3-fpm

    8. TLS with Let's Encrypt

    shell
    apt install -y certbot python3-certbot-nginx
    certbot --nginx -d forum.example.com --agree-tos -m you@example.com --redirect

    Certbot rewrites your server block to add 443 and the redirect. Renewal runs from a systemd timer:

    shell
    systemctl status certbot.timer
    certbot renew --dry-run

    If you are behind Cloudflare, use DNS-only mode for the initial issuance, then enable the proxy and set SSL mode to Full (strict).

    9. Run the installer

    Browse to https://forum.example.com. Flarum's web installer asks for:

    • Forum title
    • Database host: 127.0.0.1
    • Database name: flarum
    • Database user: flarum
    • Database password: the one you set
    • Table prefix: leave blank unless you are sharing a database
    • Admin username, email, password

    Submit. Flarum writes config.php in the install root and runs migrations.

    If you chose 2.0 with MariaDB

    After install, open /var/www/flarum/config.php and confirm the driver:

    shell
    'database' => array (
        'driver' => 'mariadb',
        'host' => '127.0.0.1',
        ...
    ),

    If it says mysql and you are running MariaDB, change it. This matters only on 2.x.

    Lock down config.php

    shell
    chmod 640 /var/www/flarum/config.php
    chown deploy:www-data /var/www/flarum/config.php

    Verify it is not web-reachable:

    shell
    curl -I https://forum.example.com/config.php

    You want a 404. If you get a 200 or a 403 from PHP, your webroot is wrong. Fix it before you go further.

    10. Configure email

    Admin panel, Email. Fill in:

    shell
    Driver:          SMTP
    Host:            smtp.postmarkapp.com
    Port:            587
    Encryption:      tls
    Username:        <provider token>
    Password:        <provider token>
    From address:    forum@example.com

    Save, then use the "Send test mail" control. If it fails, check storage/logs/flarum.log.

    Set up a cron entry so scheduled tasks (digests, cleanup) actually run:

    shell
    crontab -e -u deploy
    shell
    * * * * * cd /var/www/flarum && /usr/bin/php flarum schedule:run >> /dev/null 2>&1

    11. Extensions

    Flarum's extension model is Composer packages. Install from the CLI:

    shell
    cd /var/www/flarum
    composer require fof/upload:"*"
    php flarum cache:clear

    Then enable it in the admin panel under Extensions.

    Worth installing on a real forum:

    • fof/upload for proper file and image handling
    • fof/oauth if you want social login
    • flarum/tags (bundled) for category structure
    • fof/best-answer if you are running a support forum
    • fof/redis if you have Redis available and want a real cache and session driver
    • fof/sitemap for search visibility

    Two things to know about 2.0 extension migration:

    • blomstra/database-queue and blomstra/fontawesome are gone. That functionality is now in core.
    • blomstra/flarum-redis becomes fof/redis, and blomstra/horizon becomes fof/horizon.

    The Extension Manager extension (flarum/extension-manager) lets you install extensions from the admin UI without SSH. It is convenient. It also means your production forum runs Composer from a web request. On a small RamNode plan, that is a real timeout and memory risk. Prefer the CLI.

    12. Backups

    Flarum has no built-in backup system. You build one. Three things need backing up: the database, public/assets/ (avatars, logos, uploads), and config.php plus composer.json and composer.lock.

    /usr/local/bin/flarum-backup.sh:

    shell
    #!/bin/bash
    set -euo pipefail
    
    FLARUM_DIR=/var/www/flarum
    BACKUP_DIR=/srv/backups/flarum
    STAMP=$(date +%Y%m%d-%H%M%S)
    KEEP_DAYS=14
    
    mkdir -p "$BACKUP_DIR"
    
    # Database
    mariadb-dump --single-transaction --quick --default-character-set=utf8mb4 \
      -u flarum -p"$FLARUM_DB_PASS" flarum | gzip > "$BACKUP_DIR/db-$STAMP.sql.gz"
    
    # Assets and config
    tar czf "$BACKUP_DIR/files-$STAMP.tar.gz" \
      -C "$FLARUM_DIR" public/assets config.php composer.json composer.lock
    
    # Retention
    find "$BACKUP_DIR" -type f -mtime +$KEEP_DAYS -delete

    Put the DB password in a root-only environment file rather than in the script:

    shell
    echo 'FLARUM_DB_PASS=your-password' > /root/.flarum-backup.env
    chmod 600 /root/.flarum-backup.env

    And source it from cron:

    shell
    15 3 * * * . /root/.flarum-backup.env && /usr/local/bin/flarum-backup.sh >> /var/log/flarum-backup.log 2>&1

    Then get the backups off the box. A RamNode VPS is a single node. Backups that live on the same disk as the thing they are backing up are not backups.

    shell
    rsync -az --delete /srv/backups/flarum/ user@backup-host:/srv/backups/flarum/

    Restore

    shell
    gunzip < db-20260716-030001.sql.gz | mariadb -u flarum -p flarum
    tar xzf files-20260716-030001.tar.gz -C /var/www/flarum
    cd /var/www/flarum && php flarum cache:clear

    13. Upgrades

    Minor updates within a release line

    shell
    cd /var/www/flarum
    composer update --prefer-dist --no-plugins --no-dev -a --with-all-dependencies
    php flarum migrate
    php flarum cache:clear

    Back up first. Every time.

    1.8 to 2.0

    The order here is not negotiable. Do it on a staging copy first.

    1. Get your 1.x install fully current, including all extensions:
      shell
      composer update --prefer-dist --no-plugins --no-dev -a --with-all-dependencies
    2. Confirm PHP is 8.3 or higher and Composer is 2.x.
    3. Find extensions with no 2.0 release. Run composer why-not flarum/core 2.0.0 and remove (not disable) anything blocking. Replace superseded packages per section 11.
    4. Edit composer.json: set every extension version string to *, including bundled ones like flarum/tags and flarum/mentions. Set flarum/core to ^2.0, never to * and never to a pinned minor.
    5. Add "minimum-stability": "beta" until 2.0 ships stable, then change it back to stable.
    6. If you use MariaDB, change the driver in config.php to mariadb.
    7. Disable third-party extensions in the admin dashboard. Not required, but it makes debugging tractable.
    8. Run the update, migrate, clear cache, restart PHP-FPM and opcache.

    If the update command fails, composer why-not flarum/core 2.0.0 tells you exactly what is blocking. That is nearly always an unported extension.

    14. Troubleshooting

    White screen after install. Check storage/logs/flarum.log and /var/log/nginx/flarum.error.log. Nine times out of ten it is permissions on storage/.

    "The requested URL was not found" on every page except the homepage. URL rewriting is not working. Confirm include /var/www/flarum/.nginx.conf; is in your server block and that the file exists.

    config.php is readable from the web. Your root is pointing at the install directory instead of public/. Fix immediately and rotate the database password, because it was exposed.

    Assets do not load after an update. php flarum cache:clear, then confirm public/assets is writable by www-data.

    Random query failures on 2.0. Driver mismatch. config.php says mysql, your server is MariaDB.

    PHP-FPM 502s under modest load. pm.max_children too high, box swapped, workers got killed. Lower it. Check dmesg | grep -i oom.

    Emails silently not sending. Check storage/logs/flarum.log. Confirm you are on 587 or 465 with a real provider, because port 25 is not a route available to you on RamNode.

    15. Where Flarum fits

    Flarum is the right choice when you want a clean, fast, modern-looking forum that runs on a small box and does not demand a dedicated server. The extension ecosystem is smaller than Discourse's and the moderation tooling is less sophisticated. If you need trust levels, sophisticated spam handling, and a mature plugin catalog, Discourse is worth its heavier footprint. If you want real-time interaction and a Node stack, NodeBB sits in between. Both have companion guides.


    Quick reference

    shell
    Install root:     /var/www/flarum
    Webroot:          /var/www/flarum/public
    Config:           /var/www/flarum/config.php
    Logs:             /var/www/flarum/storage/logs/flarum.log
    nginx include:    /var/www/flarum/.nginx.conf
    PHP-FPM socket:   /run/php/php8.3-fpm.sock
    
    php flarum cache:clear
    php flarum migrate
    php flarum info
    php flarum schedule:run