Skip to content

Installing on RHEL-based systems

See also: Production install · Reverse proxy and TLS · Troubleshooting

This page is a delta, not a second manual. Follow the production install — the fifteen steps, the layout, the configuration, the verification — and substitute the commands below where they differ. Four things change: the package manager and package names, the PostgreSQL repository, SELinux, and firewalld.

Applies to RHEL 9+, Rocky Linux 9, AlmaLinux 9 and Fedora.

What changes

Step in production On RHEL
2 · base packages dnf, and EPEL for some tools
3 · media toolchain ffmpeg needs RPM Fusion; the package is ImageMagick
4 · PostgreSQL 18 the PGDG RPM repository, and dnf -qy module disable postgresql
5 · pinned Bun identical
6–9 · code, database, installer, .env identical
10–11 · media gate, proxy identical, plus SELinux contexts
12 · systemd identical, plus SELinux for the socket
everything else identical

1. Service user and directories (step 1)

useradd --system --home-dir /opt/dedalo --shell /sbin/nologin dedalo
mkdir -p /opt/dedalo /srv/dedalo/media
chown dedalo:dedalo /opt/dedalo /srv/dedalo/media
chmod 0755 /opt/dedalo

2. Base packages (step 2)

dnf install -y epel-release
dnf install -y git unzip gzip file ca-certificates curl tar

3. Media toolchain (step 3)

ffmpeg is not in the base repositories — RPM Fusion carries it.

# Rocky / AlmaLinux / RHEL 9
dnf install -y \
  https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-9.noarch.rpm \
  https://mirrors.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-9.noarch.rpm
crb enable          # CodeReady Builder (on RHEL: subscription-manager repos --enable …)

# Fedora
# dnf install -y \
#   https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm \
#   https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm

dnf install -y ffmpeg ImageMagick poppler-utils ocrmypdf

The package is ImageMagick, with capitals

dnf install imagemagick fails. And the RHEL 9 package is ImageMagick 6: it provides convert and identify but no magick binary. That is supported — the engine probes for magick first and falls back automatically. Nothing to configure.

Verify the binaries the engine will look for under /usr/bin:

command -v ffmpeg ffprobe qt-faststart convert identify pdftotext ocrmypdf

If qt-faststart is absent, set DEDALO_AV_FASTSTART_PATH in .env once you have it.

4. PostgreSQL 18 (step 4)

The distribution ships an older PostgreSQL as a module, and it wins over PGDG unless you disable it.

dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
dnf -qy module disable postgresql

dnf install -y postgresql18-server postgresql18

/usr/pgsql-18/bin/postgresql-18-setup initdb
systemctl enable --now postgresql-18

The client binaries are not on $PATH

PGDG installs them under /usr/pgsql-18/bin/, which is not on the default path — so the installer's pre-flight check reports psql not found, or, worse, resolves an older client from elsewhere and fails mid-install.

Declare them in ../private/.env:

DEDALO_PG_BIN_PATH=/usr/pgsql-18/bin

…and export the same value for the install command itself, since .env does not exist yet at that point:

sudo -u dedalo \
  DEDALO_PG_BIN_PATH=/usr/pgsql-18/bin \
  DEDALO_INSTALL_ROOT_PASSWORD='the-root-password' \
  MEDIA_PATH=/srv/dedalo/media \
  /opt/dedalo/.bun/bin/bun run scripts/install.ts 

Create the empty database and role exactly as in step 7, using sudo -u postgres /usr/pgsql-18/bin/psql.

5. Firewall (firewalld)

firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload

Do not open the database port, and do not open a port for the engine: production serving is over a unix socket, and the proxy is the only thing that should be reachable from outside.

6. SELinux

SELinux is enforcing by default, and it is why a configuration that is correct on Ubuntu can still answer 502 and 403 here. Three things need attention.

The web server must be allowed to connect out

setsebool -P httpd_can_network_connect 1

The unix socket must be reachable by the web server

Put the socket in a systemd RuntimeDirectory (as step 10 does) and label it:

semanage fcontext -a -t httpd_var_run_t '/run/dedalo(/.*)?'
restorecon -Rv /run/dedalo

The permission half of that step still applies too: UMask=0007 in the unit, and the web-server user added to the dedalo group.

The media tree and the client tree must be readable by the web server

semanage fcontext -a -t httpd_sys_content_t '/srv/dedalo/media(/.*)?'
semanage fcontext -a -t httpd_sys_content_t '/opt/dedalo/master_dedalo/client(/.*)?'
restorecon -Rv /srv/dedalo/media /opt/dedalo/master_dedalo/client

The media tree is written by the engine and read by the web server

The engine writes the generated rule files and the marker store into MEDIA_PATH; the web server only ever reads them. httpd_sys_content_t is therefore the right label — the engine writes as dedalo, unconstrained by httpd_* policy.

When something is denied and you cannot see why

ausearch -m AVC -ts recent

Read the denial before reaching for setenforce 0. Turning SELinux off makes the symptom disappear and leaves you with a server you cannot reproduce.

7. The web server

nginx is in EPEL. Apache's service is httpd (not apache2), its modules live in /etc/httpd/conf.modules.d/ and its vhosts in /etc/httpd/conf.d/. Otherwise the reverse proxy page applies unchanged — including the generated media rule files, the root rule, and the timeouts.

dnf install -y nginx                 # or: dnf install -y httpd mod_ssl
systemctl enable --now nginx

dnf install -y certbot python3-certbot-nginx     # or python3-certbot-apache
certbot --nginx -d dedalo.example.org

Everything else

Steps 5, 6, 8, 9, 10, 13, 14, 15 and 16 of the production install apply verbatim. The engine does not know which distribution it is running on.