SSD Nodes Learn Hosting plans →
Guides Matt ConnorBy Matt Connor

What XenForo needs on a VPS before you install

XenForo is licensed software, so the server comes first: the PHP version and extensions, MySQL, writable directories, cron, and the nginx rewrite.

What XenForo needs on a VPS

To install XenForo on a VPS you get the server right first, then upload the software. The server side is a current PHP 8 release with a short list of extensions, MySQL 8.0 or MariaDB 10.6 and newer, two directories the web server can write to, one cron entry, and a rewrite rule if you run nginx. XenForo is licensed software: you download the archive from the customer area of your own account after you buy a licence, so there is no public download URL and no apt package to point you at.

That is why this is a requirements and planning guide instead of a copy and paste install. Every command below is one you run on your own server. We cannot run the XenForo installer in our own test environment, because the archive needs a paid account to fetch, so nothing here claims to be a tested end to end run of the installer itself. The stack underneath it is ordinary Linux, nginx, MySQL and PHP, and the tested PHP and MySQL stack for Ubuntu 24.04 is where the verified version of those steps lives.

The licence, and what it means for planning

A XenForo licence is perpetual. It includes twelve months of software updates and ticket support from the day you buy it. After twelve months the forum keeps running with no further payment. What stops is your right to download new releases and open support tickets, and buying a twelve month extension starts both again. As of August 2026 the current release line is XenForo 2.3.

This matters on a VPS because the box outlives the support window. A board on a lapsed licence still serves pages, but it stops receiving security releases while the PHP and MySQL underneath it keep moving forward. Treat the renewal as part of the running cost of the server, next to the plan itself and your backup storage.

A few things are priced apart from the licence: the option to remove the XenForo branding link in the footer, and the official add-ons such as Media Gallery and Enhanced Search. Enhanced Search is the one with real server consequences, because it replaces MySQL search with Elasticsearch. That decision belongs in your sizing, not in your budget alone.

The PHP version and extensions XenForo needs

XenForo publishes PHP 7.2.0 as its floor and recommends a current PHP 8 release. Take the recommendation. Ubuntu 24.04 ships PHP 8.3 in its own archive, so you meet it with no third party repository and no PPA (personal package archive).

The published required extensions are MySQLi, GD with JPEG support, cURL, SimpleXML, DOM, PCRE, SPL, JSON, iconv and ctype. Most of that list is free on a modern build: PCRE, SPL and JSON are always compiled in, and iconv and ctype almost always are. The ones you install by hand are MySQLi, GD, cURL and the XML pair.

sudo apt update
sudo apt install -y nginx mysql-server php8.3-fpm php8.3-mysql php8.3-gd php8.3-curl php8.3-xml php8.3-mbstring php8.3-zip php8.3-gmp

php8.3-mysql provides MySQLi, and php8.3-xml provides both SimpleXML and DOM, so one package covers two requirements. The last packages on that line are optional. mbstring and gmp are what push notifications need, and zip lets you install add-ons from the admin control panel instead of by upload.

php -m
php --ini

php -m lists the modules loaded for the command line, which is not what your forum runs on. PHP-FPM reads /etc/php/8.3/fpm/php.ini, and the command line reads /etc/php/8.3/cli/php.ini, so a limit you raise in one does nothing in the other. XenForo publishes a small requirements checker as a zip on its site. Upload it, open it in a browser, and it reports on the PHP that will actually serve the forum. It also warns when functions such as exec, proc_open or popen are disabled in php.ini, which some server hardening guides do. A stock Ubuntu install leaves them enabled.

PHP's default memory_limit is 128M. Keep that number: it is the ceiling a single PHP worker can grow to, and it drives the sizing arithmetic further down.

The database: MySQL or MariaDB

The published floor is MySQL 5.7, MariaDB 10.2, or the matching Percona release. Ubuntu 24.04's mysql-server is 8.0 and its mariadb-server is 10.11, so either default clears it comfortably.

XenForo's installer expects an empty database and a user that can reach it.

CREATE DATABASE xenforo CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'xenforo'@'localhost' IDENTIFIED BY 'use-a-long-random-password';
GRANT ALL PRIVILEGES ON xenforo.* TO 'xenforo'@'localhost';
FLUSH PRIVILEGES;

Create it as utf8mb4 on day one. XenForo 2 stores full unicode text, so emoji in post bodies and thread titles need utf8mb4 all the way down. Converting a live board later is a table by table job you do not want to schedule.

PHP 8.3 speaks MySQL 8.0's default caching_sha2_password authentication, so skip the mysql_native_password workaround that older XenForo guides still carry. That advice is left over from PHP versions before 7.4, when the client library could not do the newer handshake.

The directories XenForo has to write to

There are two, and they are not the same kind of thing. data is served to browsers and holds avatars and attachment thumbnails. internal_data must never be served to browsers, and it holds attachments plus the files PHP writes for itself, such as the temporary directory and the compiled code cache.

The XenForo manual says to chmod both to 0777. That instruction is written for shared hosting, where you reach files over FTP and the web server runs as a user you cannot become. On a VPS you own that user, so set ownership instead of opening the permissions to everyone.

sudo chown -R www-data:www-data /srv/xenforo/data /srv/xenforo/internal_data
sudo chmod -R u=rwX,go=rX /srv/xenforo/data /srv/xenforo/internal_data

0777 means any account on the box can overwrite an avatar or drop a file into internal_data. Ownership gives PHP exactly the same write access with none of that exposure.

The third writable path is src/config.php, and only while you install. If src is not writable, the installer offers the finished file as a download and you upload it yourself, which is the better outcome anyway. Lock it afterwards so PHP can read it and nothing can rewrite it.

sudo chown root:www-data /srv/xenforo/src/config.php
sudo chmod 640 /srv/xenforo/src/config.php

The cron entry, and why the default one drifts

XenForo runs its scheduled tasks off page views. A visitor loads a page, XenForo notices a task is due, and the job runs in the background. On a busy board you never think about this. On a new board with four visitors a day, a daily cleanup task runs whenever somebody happens to arrive, so it fires at odd hours or not at all.

The fix is the Job run trigger option in the admin control panel. Set it to the server based trigger, then add a crontab entry as the same user PHP-FPM runs as.

sudo crontab -u www-data -e
* * * * * cd /srv/xenforo && /usr/bin/php cmd.php xf:run-jobs > /dev/null 2>&1

Run it once by hand first and watch it exit quietly, which is what a board with no outstanding jobs looks like.

cd /srv/xenforo && sudo -u www-data php cmd.php xf:run-jobs

Two things go wrong here. If root runs the job runner, the files it writes into internal_data end up owned by root, and the web server cannot replace them afterwards. And if you switch the trigger to server based and then forget the crontab, no scheduled task runs again, with no error anywhere, which is why the manual calls this an advanced change. Tools, then Cron entries, lists every task with its next run time. That page is where you confirm the switch worked.

The nginx rewrite that Apache instructions leave out

XenForo ships a file called htaccess.txt in its root directory. On Apache you rename it to .htaccess, mod_rewrite reads it, and /threads/some-title.123/ becomes a request for index.php. nginx does not read .htaccess files at all. That single fact breaks two separate things: the friendly URLs, and the protection on directories that should never be public.

The first symptom is easy to recognise. The forum front page loads fine, and every thread link returns nginx's own plain 404 page rather than a XenForo styled error. nginx looked for a file at /threads/some-title.123/, found nothing there, and stopped, because nothing told it to fall back to index.php.

Here is the configuration XenForo publishes, adapted for an install at the root of a site rather than in a subdirectory.

server {
    listen 443 ssl;
    server_name forum.example.com;
    root /srv/xenforo;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$uri&$args;
    }

    location ^~ /internal_data/ { internal; }
    location ^~ /src/ { internal; }
    location ^~ /install/data/ { internal; }
    location ^~ /install/templates/ { internal; }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

XenForo's own example ends the PHP block with fastcgi_pass 127.0.0.1:9000. Ubuntu's php8.3-fpm package listens on a unix socket rather than a TCP port, so use the socket path above and confirm it with ls /run/php/.

Two details in that block do more work than they look. try_files $uri $uri/ /index.php?$uri&$args puts the requested path into the query string, which is where XenForo reads the route from, so copy that exact form instead of a generic PHP snippet. And the ^~ on the internal locations is not decoration. In nginx a regular expression location such as ~ \.php$ normally beats a plain prefix location, and ^~ is what stops that from happening. Without it, a direct request for /src/config.php is handed to PHP-FPM rather than refused, which leaves your database password behind nothing but PHP behaving correctly. internal means nginx serves those paths only on its own internal redirects, and answers any direct request with 404.

If the same box terminates TLS (transport layer security) or serves other sites, the nginx location matching and proxy behaviour explained here covers the order these blocks are evaluated in.

How much RAM does a XenForo board need on a VPS?

XenForo publishes no memory figure, and that is honest, because the answer comes from your PHP worker count and your database working set rather than from the software. Do the arithmetic instead of copying somebody's number.

MySQL is the largest single consumer. Its InnoDB buffer pool caches your tables and indexes in memory, and it defaults to 128M. While the board's active data fits inside that pool, page loads run at memory speed. Once posts and their indexes outgrow it, every read that misses goes to the disk.

PHP-FPM is the variable one. Each worker process can grow to memory_limit, which is 128M by default, though rendering a normal forum page costs far less than that. The figure that matters is the worst case: pm.max_children multiplied by what a heavy request actually uses. Start workers only when they are needed.

pm = ondemand
pm.max_children = 5
pm.process_idle_timeout = 30s

That belongs in /etc/php/8.3/fpm/pool.d/www.conf. On a 1 GB box, five children is an honest ceiling once MySQL has taken its share. If the board has grown past the default cache, give MySQL a larger pool.

[mysqld]
innodb_buffer_pool_size = 256M

Save that as /etc/mysql/mysql.conf.d/xenforo.cnf and restart MySQL. For a small board, 2 GB is a comfortable floor and leaves headroom for a backup run and an upgrade. 1 GB runs a quiet, mostly text board if you cap pm.max_children, add a swap file, and keep Elasticsearch off the machine entirely.

What search and attachments do to a small box

Search strains first. XenForo's built-in search is a MySQL FULLTEXT index stored in the xf_search_index table, which holds a second copy of your searchable text plus the index over it. On a text heavy board it becomes one of the largest tables you own, and it competes for the same buffer pool as everything else. Rebuilding it, after an import or a large upgrade, is the heaviest job the board will ever run. Start that from the admin control panel so it runs as a background job, and expect page loads to be slow while it works.

XenForo Enhanced Search is the official add-on that moves search to Elasticsearch, and for the 2.3 line it needs Elasticsearch 7.2 or newer. Elasticsearch is a Java service with its own heap, and a useful heap starts around a gigabyte. There is no room for that on a 1 GB or 2 GB VPS alongside MySQL and PHP. Size the plan for it, or give it a second server. Until search feels slow or relevance is poor across a large archive, the built-in MySQL search is enough.

Attachments are the second pressure point. They live under internal_data/attachments, which is deliberately not web readable, so every download is served by PHP: the browser requests /attachments/filename-123/, XenForo checks permission, and PHP streams the bytes. That occupies a PHP-FPM worker for the entire transfer. With pm.max_children at 5, five people pulling a large file at once fill the pool, and the next visitor waits for a page that normally renders instantly. Avatars and thumbnails sit in data and are served by nginx directly, so they cost almost nothing.

Uploads carry their own memory cost. GD decompresses an image into PHP memory to build the thumbnail, so a large photo can exceed memory_limit and fail, leaving Allowed memory size of 134217728 bytes exhausted in the PHP error log. Raising memory_limit raises the worst case for every worker at once, so lower pm.max_children when you raise it. XenForo can use the ImageMagick extension instead of GD, which is worth testing on a board that takes large photos.

Set PHP's upload_max_filesize and post_max_size above the attachment size you plan to allow. XenForo's own attachment limit cannot exceed what PHP accepts, so the PHP values are the real ceiling, and a mismatch shows up as uploads that fail with no useful message in the browser.

Two log lines tell you the box is full. server reached pm.max_children setting in /var/log/php8.3-fpm.log means requests are queueing behind the worker pool. Out of memory: Killed process 1234 (mysqld) in dmesg means you raised a limit past what the machine has, and the forum will show a database connection error until MySQL restarts.

XenForo, Flarum or Discourse

XenForo costs money and the other two do not, so the licence has to buy something you want: the traditional board layout with a mature paid add-on market behind it. The upgrade path is part of that appeal. You unzip a new archive over the old files and visit /install.

XenForo and Flarum sit on the same stack of nginx, PHP-FPM and MySQL. If your VPS already runs that for another site, either one drops in beside it with no new moving parts. Flarum on a VPS is the closer comparison: open source, lighter on memory, with a much smaller extension ecosystem and fewer people who have already hit the problem you are about to hit.

Discourse is a different shape. It is Ruby, its supported install builds and runs a Docker container, and it expects more memory than either PHP forum. Discourse on a VPS covers what that stack asks for. Pick it when you want feed style discussion with trust level moderation rather than an index of forums and threads.

If you have not settled on any of them, the comparison of self-hosted forum software puts the options side by side before you spend anything.

Before you upload the archive

  • PHP 8.3 with mysqli, gd, curl and the XML extension, confirmed through the web server and not only on the command line.
  • An empty utf8mb4 database, plus a database user scoped to that database alone.
  • data and internal_data owned by the PHP-FPM user, without 0777.
  • nginx already falling back to index.php, with internal_data and src marked internal, before the site is public.
  • TLS in front of it, because the installer asks you to create the admin account and you will type that password over the network.
  • A backup that covers the database and internal_data, since attachments exist only in the filesystem.

With that in place, unzip the archive on your own machine, upload the contents of its upload directory to /srv/xenforo, and open /install in a browser.

FAQ

What are XenForo's minimum server requirements?

XenForo publishes PHP 7.2.0 or newer as its floor, with a current PHP 8 release recommended, and MySQL 5.7 or newer, where MariaDB 10.2 and the matching Percona builds also qualify. The required PHP extensions are MySQLi, GD with JPEG support, cURL, SimpleXML, DOM, PCRE, SPL, JSON, iconv and ctype. On Ubuntu 24.04 the packages that cover the list are php8.3-fpm, php8.3-mysql, php8.3-gd, php8.3-curl and php8.3-xml. XenForo also publishes a small requirements checker as a zip. Upload it and open it in a browser, because it tests the PHP the web server uses rather than the one on your command line.

Can I install XenForo without buying a licence?

No. XenForo is commercial software, and the archive is only available from the customer area of an account that holds a licence, so there is no public download link, no apt or dnf package, and no git repository. The licence is perpetual and includes twelve months of updates and ticket support. After that the forum keeps running, but downloading new releases requires a paid extension of that period. If you want a forum you can install from a public source, Flarum and Discourse are the open source options.

Will XenForo run on a 1 GB VPS?

Yes, for a quiet board that is mostly text, provided you keep the worker pool small. Set pm = ondemand and pm.max_children = 5 in the PHP-FPM pool, add a swap file, and leave the InnoDB buffer pool near its 128M default until the board grows. What does not fit in 1 GB is Elasticsearch, because a Java heap of a gigabyte cannot share the machine with MySQL and PHP. 2 GB is the more comfortable floor once a board has attachments and daily traffic.

Why do my thread URLs return 404 on nginx?

Because nginx never reads the .htaccess file XenForo ships for Apache, so nothing rewrites /threads/title.123/ into a request for index.php. Add try_files $uri $uri/ /index.php?$uri&$args; inside location / and reload nginx. The giveaway is that the front page works while every thread link returns nginx's plain 404 page instead of a styled XenForo error. While you are in that file, mark /internal_data/ and /src/ as internal with a ^~ prefix, since the .htaccess files that protect those directories on Apache also do nothing under nginx.

No. XenForo's built-in search uses a MySQL FULLTEXT index and needs no extra service. Elasticsearch arrives with XenForo Enhanced Search, a separately purchased official add-on that requires Elasticsearch 7.2 or newer for the 2.3 line. It buys better relevance and faster queries across a large archive, at the cost of a Java service with its own memory budget. Add it when searching the MySQL index becomes slow, and size the server for it at that point rather than before.

#xenforo#forum#php#hosting-requirements#self-hosting