How to deploy & self-host Flarum on DigitalOcean

How to deploy & self-host Flarum on DigitalOcean

We may earn an affiliate commission through purchases made from our guides and tutorials.

You’re going to deploy Flarum on a DigitalOcean Droplet, wire it to Nginx, PHP-FPM, and MariaDB/MySQL, and leave it in a state where upgrades and extensions won’t bite you later. Flarum’s a lean, modern PHP forum that favors extensions over bloat; the core is small, fast, and clean. Composer handles dependencies and updates, and most “heavy lifting” (feeds, sitemaps, email queues, etc.) is pushed into extensions. The trade-off is you need to set up the basics correctly the first time.

Also—quiet reminder—have a DigitalOcean account and a domain you control. We’ll assume Ubuntu 24.04 LTS on a fresh Droplet.

What you’ll set up

  • A Ubuntu 24.04 Droplet with Nginx, PHP-FPM, MariaDB/MySQL, and Composer.
  • Flarum installed to /var/www/flarum with a web root of /var/www/flarum/public.
  • HTTPS via Let’s Encrypt.
  • Correct permissions so the installer can write config.php, storage, and public/assets—and nothing more than necessary after that.
  • A cron entry for the Flarum scheduler, so extensions that rely on scheduled tasks actually run.

Along the way I’ll call out what’s “required” vs “nice to have,” and where most people mess up (spoiler: wrong Nginx root, missing PHP extensions, or bad permissions).

Create the Droplet and log in

Spin up an Ubuntu 24.04 LTS Droplet (Basic plan is fine to start; forums scale more on I/O and caching than raw CPU). Add your SSH key and log in as root, then create a non-root sudo user.

adduser deploy
usermod -aG sudo deploy
rsync -a ~/.ssh/ /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
su - deploy

Lock down the firewall:

sudo ufw allow OpenSSH
sudo ufw allow "Nginx Full"
sudo ufw enable
sudo ufw status

System packages, PHP (with extensions), Nginx, and MariaDB

Flarum runs on PHP with common extensions. The official docs list the baseline PHP extensions and DB support; on Ubuntu you’ll install PHP-FPM plus the usual suspects (mbstring, json, gd, curl, dom, fileinfo, tokenizer, zip, pdo_mysql).

sudo apt update && sudo apt -y upgrade
sudo apt -y install nginx mariadb-server
sudo apt -y install php-fpm php-cli php-mbstring php-json php-gd php-curl php-dom php-fileinfo php-tokenizer php-zip php-mysql

(If you prefer PostgreSQL, Flarum’s PDO layer supports it via extensions, but MySQL/MariaDB is the well-trodden path per docs.)

Tweak a couple PHP-FPM defaults for headroom:

sudo sed -i 's/^;cgi.fix_pathinfo=.*/cgi.fix_pathinfo=0/' /etc/php/*/fpm/php.ini
sudo systemctl reload php*-fpm

Install Composer (globally) and prepare the web root

Flarum is installed and managed via Composer. That’s not optional.

cd ~
php -r "copy('https://getcomposer.org/installer','composer-setup.php');"
php composer-setup.php --install-dir=/usr/local/bin --filename=composer
rm composer-setup.php
composer --version

Create the target directory and set sane ownership. You’ll let the web user own only what Flarum needs to write.

sudo mkdir -p /var/www/flarum
sudo chown -R deploy:www-data /var/www/flarum
sudo chmod 775 /var/www/flarum
cd /var/www/flarum

Create the Flarum project

Install Flarum into the current directory. (If Composer warns about memory, add temporary swap.)

composer create-project flarum/flarum . 

If you see permission warnings during the web installer, remember: during install Flarum must be able to write the root install path (to create config.php), storage/, and public/assets/. After install, only storage/ and public/assets/ need to remain writable by the web user.

Make sure the web server can write those:

sudo chown -R www-data:www-data /var/www/flarum/storage /var/www/flarum/public/assets
sudo chgrp -R www-data /var/www/flarum
sudo chmod -R 775 /var/www/flarum/storage /var/www/flarum/public/assets

(If the installer complains, it’s almost always these three paths. Fix ownership, not just chmod.)

Create the database and user

sudo mysql

Inside the MariaDB shell:

CREATE DATABASE flarum CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'flarum_user'@'localhost' IDENTIFIED BY 'a-strong-password';
GRANT ALL PRIVILEGES ON flarum.* TO 'flarum_user'@'localhost';
FLUSH PRIVILEGES;
EXIT;

(Any name is fine; utf8mb4 is your friend for emojis and international text.)

Nginx server block (serve /public, not the project root)

The only public files live under /var/www/flarum/public. Point Nginx there and rewrite everything else through index.php. This keeps source code out of the web root, as the docs recommend.

sudo tee /etc/nginx/sites-available/flarum.conf >/dev/null <<'NGINX'
server {
    listen 80;
    listen [::]:80;
    server_name forum.example.com;

    root /var/www/flarum/public;
    index index.php;

    # Main router: send non-files to index.php
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    # PHP-FPM
    location ~ \.php$ {
        include snippets/fastcgi-php.conf;
        # Adjust socket for your PHP version:
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
    }

    # Deny direct access to hidden files
    location ~ /\. {
        deny all;
    }
}
NGINX

sudo ln -s /etc/nginx/sites-available/flarum.conf /etc/nginx/sites-enabled/flarum.conf
sudo nginx -t && sudo systemctl reload nginx

That try_files pattern is exactly what you want here; it checks the filesystem and falls back to index.php so Flarum’s router can resolve /api, /admin, and pretty URLs.

Run the web installer

Visit http://forum.example.com. The installer asks for:

  • Database: the DB name, user, and password you created above.
  • Admin account: your initial user.
  • Base URL: https://forum.example.com (use HTTPS once certificates are installed).

If you get a “Hold up, directory not writable” message, fix ownership on the three paths mentioned earlier and reload.

HTTPS with Let’s Encrypt

Install Certbot’s Nginx plugin and get a certificate:

sudo apt -y install certbot python3-certbot-nginx
sudo certbot --nginx -d forum.example.com --redirect --agree-tos -m you@example.com
sudo systemctl status certbot.timer

You should see a renewal timer in place. (Any generic, well-documented Nginx+Certbot flow is fine here.)

Flarum scheduler (cron) — do not skip

Flarum itself doesn’t run scheduled tasks by default; extensions use the scheduler for things like sitemaps, drafts, and housekeeping. Add a cron job that runs every minute and lets Flarum decide what should fire.

crontab -e

Add:

* * * * * cd /var/www/flarum && php flarum schedule:run >> /dev/null 2>&1

You can list registered scheduled tasks to confirm extensions hooked in:

cd /var/www/flarum
php flarum schedule:list

That command only lists; it doesn’t run them.

Note: Some extensions also use queues (e.g., for emails or uploads). Many sites get by with the scheduler alone. If you later add a real queue backend (Redis, etc.), you can run a worker or use a “work and exit” cron pattern.

Post-install hardening and maintenance

Ownership & permissions (after install)
You can tighten the root install path once config.php exists; keep only storage/ and public/assets/ writable by the web user. Don’t let laziness turn into www-data owning everything recursively forever.

sudo chown deploy:deploy /var/www/flarum/config.php
sudo chown -R www-data:www-data /var/www/flarum/storage /var/www/flarum/public/assets
sudo find /var/www/flarum -type d -exec chmod 755 {} \;
sudo find /var/www/flarum -type f -exec chmod 644 {} \;
sudo chmod -R 775 /var/www/flarum/storage /var/www/flarum/public/assets

Clear cache safely (when installing/updating extensions)
Run cache clears as the web user; if you used sudo earlier and polluted the cache with root-owned files, fix it:

cd /var/www/flarum
sudo -u www-data -- php flarum cache:clear

If things get weird, nuke the cache files and try again.

Upgrades
Stick to Composer updates from the project root:

cd /var/www/flarum
composer update --no-dev -o
php flarum cache:clear

Backups
Dump the DB and archive /var/www/flarum (you can always rebuild vendor/ via Composer, but keep composer.json, composer.lock, config.php, and assets).

Common pitfalls (and straight fixes)

  • Wrong Nginx root — must be /var/www/flarum/public. If you point to the project root, you’ll expose source and break routing.
  • Missing PHP extensions — installer nags about mbstring, json, gd, curl, dom, fileinfo, tokenizer, zip, pdo_mysql. Don’t fight it; install them.
  • Permissions loop — errors tell you exactly which path isn’t writable. Fix ownership to the web user and only on storage/, public/assets/, and (during install) the root path to write config.php.
  • No scheduled tasks firing — cron must run php flarum schedule:run every minute. The schedule frequency is controlled by the extension, not cron.
  • Subdirectory installs — possible, but be precise with try_files and the sub-path in the root/rewrites; otherwise you’ll chase 404s all day.

Optional: sample Nginx + PHP-FPM tune

If you see 502s under burst traffic, bump PHP-FPM pool size:

sudo nano /etc/php/8.3/fpm/pool.d/www.conf

Change (example—size it to RAM/CPU realistically):

pm = dynamic
pm.max_children = 20
pm.start_servers = 4
pm.min_spare_servers = 4
pm.max_spare_servers = 8

Then:

sudo systemctl reload php8.3-fpm

Sanity checks

  • curl -I https://forum.example.com returns 200 or 302 (to /all or similar), and the page loads fast.
  • php flarum schedule:list shows registered jobs after you enable extensions that use them.
  • New users receive email (if you configured SMTP in Admin → Email; if not, do that sooner rather than later). If messages stall, a queue-related extension may need a worker or the scheduler fix above.

That’s it. Straightforward, maintainable, and future-proof enough that you won’t hate yourself in six months when you add five extensions and a theme. If you want, you can layer in Redis for cache/sessions and a proper queue worker next—only when you actually need it.

Was this helpful?

Thanks for your feedback!
Alex is the resident editor and oversees all of the guides published. His past work and experience include Colorlib, Stack Diary, Hostvix, and working with a number of editorial publications. He has been wrangling code and publishing his findings about it since the early 2000s.

Leave a comment

Your email address will not be published. Required fields are marked *