Aakvatech Limited - Installing Frappe Pilot Cleanly on Ubuntu: Avoid Redis, Python, MariaDB and Node.js Dependency Failures

Installing Frappe Pilot on a fresh Ubuntu server can fail surprisingly late if host dependencies are incomplete. This guide shows how to validate the server first and avoid repeated bench initializati

 · 10 min read

Frappe Pilot is designed around a sensible operating model: prepare the server once with privileged access, then allow the dedicated bench user to manage normal Pilot operations without requiring unrestricted root access.

In practice, however, a partially prepared Ubuntu server can reach the Pilot bench initialization wizard before every required dependency has been validated.

The result can be a frustrating sequence:

Start Pilot bench initialization
        ↓
Redis missing
        ↓
Retry
        ↓
Build libraries missing
        ↓
Retry
        ↓
Python.h missing
        ↓
Retry
        ↓
Node.js missing
        ↓
Retry
        ↓
Node.js 22 installed, but Frappe requires >=24
        ↓
Retry again

The better approach is simple:

Validate and provision the complete host before creating the first Pilot bench.

This article documents a clean installation procedure based on real installation failures encountered while deploying current Frappe v16 workloads through Pilot.

Why this matters

A failed Pilot initialization is generally recoverable. Pilot rolls back the partially created bench directories and preserves bench.toml.

That is useful, but repeatedly discovering dependencies halfway through setup wastes time and can obscure the real problem.

The important distinction is between:

  • host provisioning, which may require root;
  • Pilot installation, which should run under the dedicated Pilot/bench user;
  • bench initialization, which should not suddenly require unrestricted privileged package installation.

Pilot's installer itself follows a two-stage model: the privileged installation prepares the host and bench user, after which Pilot is intended to operate without general root access.

The installation failures we encountered

Several separate failures exposed the same broader requirement: a fresh server should pass a complete prerequisite check before bench initialization starts.

1. Redis was missing

Pilot initialization stopped with:

Required: redis-server. No passwordless sudo available;
install manually with your system package manager,
then re-run this command.

Redis is a host-level dependency.

Pilot should find either:

redis-server

or, on distributions using Valkey:

valkey-server

before the first bench is initialized.


2. Required build packages were missing

The next failure reported:

Missing system packages:
build-essential,
pkg-config,
libmariadb-dev

These are required because several Python dependencies used by Frappe contain native extensions that must be compiled.

A working Python interpreter alone is therefore not enough.


3. Python existed, but Python.h did not

The most instructive failure occurred while building:

mysqlclient==2.2.7

The compiler failed with:

fatal error: Python.h: No such file or directory

The selected interpreter was Python 3.14, but:

ls -l /usr/include/python3.14/Python.h

returned:

No such file or directory

This highlights an important rule:

The development headers must match the actual Python interpreter selected for the bench.

Installing generic:

apt install python3-dev

does not necessarily guarantee that the headers for Python 3.14 are installed.

For Python 3.14, verify:

ls -l /usr/include/python3.14/Python.h

and install:

apt install -y python3.14-dev

when required.


4. Node.js was completely missing

Pilot later stopped with:

Node.js is not installed.
Re-run install.sh as root to install it,
or install it yourself.

That is another host-level prerequisite that should be validated before the bench setup starts.


5. Node.js existed, but was too old

After Node.js was installed, Yarn produced:

error frappe-framework@:
The engine "node" is incompatible with this module.
Expected version ">=24". Got "22.22.1"

This is an important distinction.

Checking:

which node

is not enough.

The installer must also validate:

node --version

For the Frappe version involved in this deployment, Node.js needed to satisfy:

>=24

A host with Node.js 22 therefore passes a simple executable check but still cannot build the Frappe frontend.

Another hidden source of problems: broken APT repositories

While upgrading Node.js, the NodeSource setup script failed during:

apt update

because an unrelated MariaDB MaxScale repository returned:

404 Not Found

The active repository was:

https://dlm.mariadb.com/repo/maxscale/latest/apt

This demonstrates an important operational rule:

apt update must be completely healthy before starting a Pilot installation.

A broken third-party repository can prevent the installation of an entirely unrelated component.

For example, NodeSource may be configured correctly, but its installer cannot complete because MariaDB MaxScale has an invalid repository entry.

The following process is recommended for a new Ubuntu Pilot server.

Step 1: Prefer Ubuntu LTS

For production installations, prefer a stable long-term-support release such as:

Ubuntu 24.04 LTS

Very recent interim Ubuntu releases may not yet be supported by every third-party repository used by the stack.

Check:

cat /etc/os-release

Pay particular attention to:

PRETTY_NAME
VERSION_CODENAME

Step 2: Validate APT first

Before installing Pilot:

apt update

Do not proceed if you see:

404 Not Found
does not have a Release file
Malformed stanza
repository is not signed

Inspect third-party repositories:

ls -la /etc/apt/sources.list.d/

For MariaDB-related entries:

grep -R -n "mariadb\|maxscale" \
    /etc/apt/sources.list \
    /etc/apt/sources.list.d/ 2>/dev/null

Disabling an unused MaxScale repository

Ubuntu now commonly uses deb822 .sources files.

A stanza may look like:

# MariaDB MaxScale
X-Repolib-Name: MariaDB MaxScale
Enabled: yes
Types: deb
URIs: https://dlm.mariadb.com/repo/maxscale/latest/apt
Suites: <ubuntu-codename>
Components: main
Signed-By: /etc/apt/keyrings/mariadb-keyring.gpg

If MaxScale is not required and its repository is unsupported, keep the complete stanza but change:

Enabled: yes

to:

Enabled: no

Do not leave an incomplete deb822 stanza.

Then run:

apt update

again.

Step 3: Install the base build dependencies

Install:

apt install -y \
    git \
    curl \
    bash \
    sudo \
    ca-certificates \
    build-essential \
    pkg-config \
    tzdata \
    python3 \
    python3-dev

Verify:

git --version
gcc --version
pkg-config --version
python3 --version

Step 4: Install MariaDB client development libraries

Frappe's Python dependency stack may need to compile mysqlclient.

Install:

apt install -y \
    libmariadb-dev \
    libmariadb-dev-compat

Verify:

pkg-config --exists libmariadb \
    && echo "MariaDB development libraries: OK"

Check the actual compiler flags:

pkg-config --cflags libmariadb
pkg-config --libs libmariadb

Step 5: Validate the selected Python version

Determine which version the planned bench will use.

For Python 3.14:

python3.14 --version

Then verify the compiler headers:

test -f /usr/include/python3.14/Python.h \
    && echo "Python 3.14 headers: OK" \
    || echo "Python 3.14 headers: MISSING"

If missing:

apt install -y python3.14-dev

Then verify again:

ls -l /usr/include/python3.14/Python.h

The rule is:

Python 3.14 runtime
        +
Python 3.14 development headers

not merely:

Python runtime
        +
some version of python3-dev

Step 6: Install Redis

Install:

apt install -y redis-server

Verify:

which redis-server
redis-server --version

Pilot runs bench-specific Redis instances, so the distribution-provided service can be disabled while retaining the binary:

systemctl disable --now redis-server

Verify again:

redis-server --version

Step 7: Install Node.js 24 explicitly

Do not rely on the Ubuntu package version without checking it.

First:

node --version

If it reports Node 22 while Frappe requires Node 24, upgrade it before Pilot bench initialization.

Make sure:

apt update

works cleanly.

Then configure NodeSource for Node 24:

curl -fsSL \
    https://deb.nodesource.com/setup_24.x \
    -o /tmp/nodesource_setup.sh

bash /tmp/nodesource_setup.sh

Install:

apt install -y nodejs

Verify:

node --version
npm --version

Expected:

v24.x.x

If it still reports Node 22, inspect:

apt-cache policy nodejs

before proceeding.

Step 8: Perform a full preflight

Before Pilot installation or bench creation, run:

echo "=== Operating System ==="
grep -E '^(PRETTY_NAME|VERSION_CODENAME)=' /etc/os-release

echo
echo "=== APT ==="
apt update

echo
echo "=== Git ==="
git --version

echo
echo "=== Compiler ==="
gcc --version | head -1

echo
echo "=== pkg-config ==="
pkg-config --version

echo
echo "=== MariaDB development headers ==="
pkg-config --exists libmariadb \
    && echo "libmariadb: OK" \
    || echo "libmariadb: MISSING"

echo
echo "=== Redis ==="
redis-server --version

echo
echo "=== Node.js ==="
node --version

echo
echo "=== npm ==="
npm --version

echo
echo "=== Python ==="
python3 --version

For a Python 3.14 bench:

echo
echo "=== Python 3.14 ==="
python3.14 --version

test -f /usr/include/python3.14/Python.h \
    && echo "Python 3.14 development headers: OK" \
    || echo "Python 3.14 development headers: MISSING"

Do not proceed until every dependency required by the intended bench passes.

Step 9: Run the Pilot installer as root

For upstream Pilot:

curl -fsSL \
    https://raw.githubusercontent.com/frappe/pilot/develop/install.sh \
    -o /tmp/pilot-install.sh

bash /tmp/pilot-install.sh

Pilot's installer is intentionally structured around two phases.

The root phase handles system-level preparation such as:

  • system packages;
  • database engines;
  • Redis;
  • Node.js;
  • nginx;
  • certbot;
  • process-management dependencies;
  • bench-user creation;
  • systemd lingering;
  • restricted sudo rules.

After this phase, it instructs the administrator to switch to the bench user.

Step 10: Switch to the bench user

For the standard user:

su - frappe

If you deliberately configured another user, use that user instead.

Check:

whoami

Step 11: Run the Pilot installer as the bench user

Run:

curl -fsSL \
    https://raw.githubusercontent.com/frappe/pilot/develop/install.sh \
    -o /tmp/pilot-install.sh

bash /tmp/pilot-install.sh

Pilot itself should not normally be installed and operated as root.

Step 12: Verify the environment from the bench user's shell

This catches PATH differences between root and the Pilot user.

Run:

which pilot
which python3
which node
which npm
which redis-server
which git
which gcc
which pkg-config

Then:

node --version
npm --version
redis-server --version
python3 --version

Node must still satisfy the Frappe requirement.

Also check systemd lingering:

loginctl show-user "$(whoami)" -p Linger

Expected:

Linger=yes

Step 13: Only now initialize the first bench

At this point the host should already satisfy:

APT repository health             OK
Git                               OK
Compiler/build-essential          OK
pkg-config                        OK
MariaDB development libraries     OK
Selected Python headers           OK
Redis or Valkey                   OK
Node.js >= required version       OK
npm                               OK
systemd user lingering            OK
Pilot bench user                  OK

Only then create the first bench through Pilot.

What Pilot itself could improve

The installation experience also suggests a useful architectural improvement for Pilot.

Instead of discovering each dependency during different initialization stages, Pilot could perform one complete host preflight before doing any destructive or time-consuming work.

For example:

Checking host requirements...

[OK]      Git
[OK]      GCC
[OK]      pkg-config
[OK]      MariaDB development libraries
[OK]      Redis 7.x
[OK]      Python 3.14.4
[MISSING] Python 3.14 development headers
[INVALID] Node.js 22.22.1 — requires >=24

Host is not ready for bench initialization.

This would be significantly better than:

Initialize
→ fail
→ install dependency
→ retry
→ fail later
→ install another dependency
→ retry

Node.js should be version-validated

A check such as:

if not which("node"):
    raise BenchError("Node.js is missing")

is insufficient.

Pilot should also inspect:

node --version

and compare it with the Frappe version's required Node engine.

Python headers should match the interpreter

Checking only whether:

python3-dev

is installed is likewise insufficient.

Pilot should verify the actual header path belonging to the Python executable selected for the bench.

For Python 3.14, that means validating something equivalent to:

/usr/include/python3.14/Python.h

APT health should be checked before repository installers

Before invoking NodeSource, MariaDB repositories, or other package provisioning, the installer should confirm that the package manager itself can successfully refresh repositories.

This makes failures from unrelated third-party repositories visible at the correct stage.

A practical preflight checklist

Before clicking Initialize Bench, confirm:

  • [ ] apt update completes without errors.
  • [ ] No unsupported third-party repository is enabled.
  • [ ] git --version works.
  • [ ] gcc --version works.
  • [ ] pkg-config --version works.
  • [ ] pkg-config --exists libmariadb succeeds.
  • [ ] The selected Python interpreter exists.
  • [ ] The matching Python.h exists.
  • [ ] redis-server --version or the equivalent Valkey command works.
  • [ ] node --version meets Frappe's required version.
  • [ ] npm --version works.
  • [ ] Pilot is running under the intended bench user.
  • [ ] systemd lingering is enabled.

Final recommendation

Treat Pilot installation as host provisioning first, bench creation second.

Do not start the first bench merely because the Pilot web interface is available.

A server can have Pilot installed while still lacking the compiler headers, Redis binary, correct Node.js version, or Python development files needed by Frappe.

The preferred sequence is:

Fresh supported Ubuntu server
        ↓
Validate APT
        ↓
Install host dependencies
        ↓
Install matching Python headers
        ↓
Install Redis
        ↓
Install Node.js 24
        ↓
Run complete preflight
        ↓
Run Pilot host installer as root
        ↓
Switch to Pilot user
        ↓
Install/operate Pilot as that user
        ↓
Verify environment again
        ↓
Initialize first bench

Doing this once at the start is considerably easier than troubleshooting dependencies one failure at a time during bench initialization.

Reference articles and discussions


Aakvatech Limited is a Frappe Gold Partner and ERPNext implementation company headquartered in Dar es Salaam, Tanzania, operating across East Africa and the UAE.

This article was co-created using AI to accelerate drafting, with final insights curated and validated by the author. Any customer, personal, or sensitive data referenced during drafting has been anonymized or masked where applicable. All contributors, reference URLs, tools, and materials used to assist this content curation are credited in the Reference section.


No comments yet.

Add a comment
Ctrl+Enter to add comment