Skip to content

config: make the install/config directory a compile-time option (PIHOLE_INSTALL_DIR) - #2955

Open
cyberb wants to merge 1 commit into
pi-hole:developmentfrom
cyberb:pr/configurable-install-dir
Open

config: make the install/config directory a compile-time option (PIHOLE_INSTALL_DIR)#2955
cyberb wants to merge 1 commit into
pi-hole:developmentfrom
cyberb:pr/configurable-install-dir

Conversation

@cyberb

@cyberb cyberb commented Jul 12, 2026

Copy link
Copy Markdown

What

Introduce a single compile-time base directory, PIHOLE_INSTALL_DIR (default /etc/pihole), and route the hardcoded configuration/state paths through it: pihole.toml, the legacy config, dnsmasq.conf, the hosts dir, custom.list, dhcp.leases, backups, cli_pw, versions, the inotify watch dir, and the embedded dnsmasq CONFFILE.

Packagers can now relocate the whole tree at configure time:

cmake -DPIHOLE_INSTALL_DIR=/opt/pihole/etc ...

Why

Everything currently assumes a hardcoded /etc/pihole. Packages that install FTL under a different prefix - snaps, /opt installs, read-only-root or otherwise confined images - have to patch the sources. A single overridable base directory removes that need.

Notes

  • Default is unchanged, so existing builds and installs behave exactly as before.
  • Build-time knob only - this deliberately does not add a runtime override. Runtime-settable paths (e.g. files.database, files.log.*) keep their existing config keys, and files.pid stays fixed per GHSA-6w8x-p785-6pm4.
  • The default lives in a #ifndef guard in the new src/install_paths.h, and the top-level CMakeLists.txt exposes it as a cache variable + global compile definition so it also reaches the vendored dnsmasq CONFFILE.

Happy to adjust the naming (PIHOLE_INSTALL_DIR vs PIHOLE_ETC_DIR/PIHOLE_CONFIG_DIR) to your preference.

@cyberb
cyberb requested a review from a team as a code owner July 12, 2026 13:28
@darkexplosiveqwx

Copy link
Copy Markdown
Contributor

I think this should also override the /etc/pihole/ of the default config options.
Like the default value of files.gravity being PIHOLE_INSTALL_DIR/gravity.db

@cyberb
cyberb force-pushed the pr/configurable-install-dir branch from 7461f64 to 10de901 Compare July 12, 2026 15:32
@cyberb

cyberb commented Jul 12, 2026

Copy link
Copy Markdown
Author

Good call — done. I pushed a change that also routes the /etc/pihole defaults of the runtime-settable options through PIHOLE_INSTALL_DIR: files.database, files.tmp_db, files.gravity, files.macvendor and webserver.tls.cert.

So now every /etc/pihole path in the tree derives from the base dir. I left the /var/log/pihole log defaults (files.log.*) alone since that's a different base directory — happy to give those the same treatment (e.g. a PIHOLE_LOG_DIR) if you'd like that in scope too.

@DL6ER DL6ER left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dual default works well: the CMake cache variable plus the #ifndef fallback in install_paths.h means every TU resolves the same value even when compiled without the -D, keeping files.pid fixed per GHSA-6w8x-p785-6pm4 is correct, and the default build stays byte-for-byte unchanged. One issue needs resolving before we merge, plus a few smaller things.

Teleporter export and import become asymmetric for relocated installs

In src/zip/teleporter.c the archive stores each file under its absolute path minus the leading slash, but the import side matches hardcoded entry names:

const char *extract_files[] = {
    "etc/pihole/pihole.toml",   // fixed
    "etc/pihole/dhcp.leases",   // fixed
    config.files.gravity.v.s... // derived, matches the export side
};

This PR makes the export names depend on PIHOLE_INSTALL_DIR: dhcp.leases via the changed file_path = DHCPLEASESFILE, and pihole.toml transitively through GLOBALTOMLPATH. So with -DPIHOLE_INSTALL_DIR=/opt/pihole/etc the export writes opt/pihole/etc/pihole.toml and opt/pihole/etc/dhcp.leases, while import still only accepts etc/pihole/.... The result is that config and DHCP-lease import silently no-op on a relocated build (they fall into the "Skipping file ..." branch), both on a same-host round-trip and when moving an archive between differently-configured installs.

We should settle the intended contract and make both sides match. If Teleporter archives are meant to stay portable across installs, i.e., the way backups get moved between machines, the canonical etc/pihole/... names should stay fixed on both sides regardless of PIHOLE_INSTALL_DIR, which argues for reverting the dhcp.leases change rather than extending it. If instead archives are treated as install-local, the import extract_files[] entries need to be derived from PIHOLE_INSTALL_DIR too. The fixed-name option is the more robust of the two, as it keeps existing archives importable on any build. Either way the current half-state is the one thing we cannot keep. The gravity and FTL-db entries already derive from the runtime config value symmetrically, so only pihole.toml and dhcp.leases are affected.

Minor

  • src/install_paths.h: the header comment says "Compile-time installation path prototypes", which is copy-paste from a function-prototype header, as this file declares no prototypes. "Compile-time installation paths" fits better.
  • src/config/dnsmasq_config.c: line 248 now emits GLOBALTOMLPATH, but a few lines down the same generated header still hardcodes "...in /etc/pihole/pihole.toml)". The partial change leaves the generated dnsmasq.conf header inconsistent on a relocated build, so both should use GLOBALTOMLPATH.
  • src/args.c: the --gen-x509 help examples still print /etc/pihole/tls.pem, which is now inconsistent with the relocated default we show for webserver.tls.cert. Low priority, but worth aligning here.
  • The /etc/pihole mentions left in descriptive help and comment strings (the cli_pw help, the inotify debug description, the config-backup comment) are prose and fine to leave.

Tests

Nothing in CI exercises a non-default PIHOLE_INSTALL_DIR, which is exactly the configuration where the teleporter gap shows up. A minimal relocated build plus a Teleporter export/import round-trip would catch it, worth adding once the contract above is settled.

Naming

PIHOLE_INSTALL_DIR reads fine. Since the value is the config and state root rather than a general install prefix (the log dir lives elsewhere by design), PIHOLE_CONFIG_DIR would be marginally more precise, but we do not feel strongly either way.

@jacklul

jacklul commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Wouldn't also introducing an install prefix (+ support for it on the scripts side) be good here? This would open the possibilities for custom installs/packaging a bit more.

# defaults:
PIHOLE_INSTALL_PREFIX=
PIHOLE_INSTALL_DIR=$PIHOLE_INSTALL_PREFIX/etc/pihole
PIHOLE_SCRIPTS_DIR=$PIHOLE_INSTALL_PREFIX/opt/pihole
PIHOLE_BIN_DIR=$PIHOLE_INSTALL_PREFIX/usr/local/bin
PIHOLE_TMP_DIR=$PIHOLE_INSTALL_PREFIX/tmp

One could simple relocate it under new prefix:

PIHOLE_INSTALL_PREFIX=/opt

Or change locations of specifics only:

PIHOLE_INSTALL_DIR=/new/etc/pihole

I've been maintaining Entware package of Pi-hole for a while now and I simply patch all the paths so this PR has caught my interest as it can make my work a bit easier.

Just dropping an idea here...

@darkexplosiveqwx

Copy link
Copy Markdown
Contributor

Compared to jacklul, I am doing some RPM packaging, but I am also patching many of the paths.
The main difference in RPM packaging is that it doesn't give Pi-hole its own prefix (like under /opt), but instead aims to follow the Filesystem Hierarchy Standard (FHS).
The paths I am patching:
/usr/local/bin/pihole -> /usr/bin/pihole for gravity
/var/www/html -> /usr/share/pihole/www as the webroot default (on Fedora only Apache ("httpd") should own /var/www/html)
/etc/pihole/{pihole-FTL.db,pihole-tmp.db,gravity.db,dhcp.leases,config_backups/} -> /var/lib/pihole{pihole-FTL.db,pihole-tmp.db,gravity.db,dhcp.leases,config_backups/}
/etc/pihole/{macvendor.db,versions} -> /usr/share/pihole/{macvendor.db,versions}

of course they are not that complicated to patch, but less downstream patches are always preffered.

For FHS the following variables may be useful:

Variable Upstream default Intended FHS
PIHOLE_BIN_DIR /usr/local/bin /usr/bin
PIHOLE_CONFIG_DIR /etc/pihole /etc/pihole
PIHOLE_DATA_DIR /etc/pihole /var/lib/pihole
PIHOLE_SHARE_DIR /etc/pihole /usr/share/pihole
PIHOLE_LOG_DIR /var/log/pihole /var/log/pihole
How I imagine paths to be mapped using those variables
Source file Variable Expanded path
src/config/config.h PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/pihole.toml
src/config/config.h PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/pihole-FTL.conf
src/config/config.h PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/setupVars.conf
src/config/config.h PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/migration_backup_v6
src/config/config.c PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/tls.pem
src/config/dnsmasq_config.h PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/dnsmasq.conf
src/config/dnsmasq_config.h PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/dnsmasq.conf.temp
src/config/dnsmasq_config.h PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/hosts
src/config/dnsmasq_config.h PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/hosts/custom.list
src/config/dnsmasq_config.h PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/custom.list
src/config/dnsmasq_config.c PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/pihole.toml
src/config/password.c PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/cli_pw
src/config/inotify.c PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR
src/dnsmasq/CMakeLists.txt PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/dnsmasq.conf
src/config/config.c PIHOLE_DATA_DIR PIHOLE_DATA_DIR/pihole-FTL.db
src/config/config.c PIHOLE_DATA_DIR PIHOLE_DATA_DIR/pihole-tmp.db
src/config/config.c PIHOLE_DATA_DIR PIHOLE_DATA_DIR/gravity.db
src/config/dnsmasq_config.h PIHOLE_DATA_DIR PIHOLE_DATA_DIR/dhcp.leases
src/files.h PIHOLE_DATA_DIR PIHOLE_DATA_DIR/config_backups
src/api/info.c PIHOLE_SHARE_DIR PIHOLE_SHARE_DIR/versions
src/config/config.c PIHOLE_SHARE_DIR PIHOLE_SHARE_DIR/macvendor.db
src/config/config.c PIHOLE_SHARE_DIR PIHOLE_SHARE_DIR/www
src/config/config.c PIHOLE_LOG_DIR PIHOLE_LOG_DIR/FTL.log
src/config/config.c PIHOLE_LOG_DIR PIHOLE_LOG_DIR/pihole.log
src/config/config.c PIHOLE_LOG_DIR PIHOLE_LOG_DIR/webserver.log
src/config/legacy_reader.c PIHOLE_LOG_DIR PIHOLE_LOG_DIR/FTL.log
src/api/action.c PIHOLE_BIN_DIR PIHOLE_BIN_DIR/pihole
src/args.c PIHOLE_CONFIG_DIR PIHOLE_CONFIG_DIR/tls.pem
src/api/docs/content/specs/config.yaml same as config.c same as config.c

Compatibilty with cyberb's and jacklul's ideas:
If the FHS-compatible prefixes are not defined, the default value (/etc/pihole/, or var/log...) can be prefixed with a PIHOLE_INSTALL_PREFIX. A PIHOLE_INSTALL_PREFIX can also apply to all the tmp paths.

(+ support for it on the scripts side)

This may be a bit more involved as the core scripts do not have a compilation/build step and are instead used directly.

DL6ER raised the concern of teleporter incompatibilities, it should always use the default /etc/pihole and then apply them to the configured path.

Again just some thoughts, open to some suggestions to fit you packaging needs.

@DL6ER

DL6ER commented Aug 15, 2026

Copy link
Copy Markdown
Member

Two of the open points were ours to decide.

Teleporter. Archives should stay portable across installs, so the canonical etc/pihole/... entry names stay fixed on both sides regardless of PIHOLE_INSTALL_DIR. Decoupling the archive entry name from the on-disk source path is enough:

mz_zip_writer_add_file(zip, "etc/pihole/pihole.toml", GLOBALTOMLPATH, ...);
mz_zip_writer_add_file(zip, "etc/pihole/dhcp.leases", DHCPLEASESFILE, ...);

Both names currently come from file_path+1, so a relocated build writes opt/pihole/etc/... while the import side still matches the fixed names in extract_files[], and config and lease import silently no-op. This affects pihole.toml as well, transitively through GLOBALTOMLPATH.

Scope. We would like to take PIHOLE_INSTALL_DIR as it is. Where our files should live is a far broader question, touching the core scripts and the web tree rather than only FTL, and it deserves a proper discussion instead of being settled inside this PR. pi-hole/pi-hole#5844 is the open thread for it, with pi-hole/pi-hole#2976 as earlier background, and our Discourse is the better venue if it needs a wider audience. We are happy to take an FHS-style split as a follow-up once that is decided.

Remaining items, all small:

  1. src/api/docs/content/specs/config.yaml still hardcodes the five defaults this PR makes configurable (cert, database, tmp_db, gravity, macvendor), plus two mentions in lists.yaml. We serve that spec ourselves, so a relocated install documents paths it does not use.
  2. src/config/dnsmasq_config.c:253 still hardcodes "...in /etc/pihole/pihole.toml)" just below the hunk that switched to GLOBALTOMLPATH.
  3. The --gen-x509 help in src/args.c still prints /etc/pihole/tls.pem.
  4. install_paths.h says "Compile-time installation path prototypes", but declares none.
  5. Worth rejecting a relative or trailing-slash value in CMake, as the teleporter strips the leading / by offset.

Please also rebase - the failing checks are test/api/test_api.py counter assertions on a July base and unrelated to your change, but that is not visible from the PR page.

…LE_INSTALL_DIR)

All of Pi-hole's configuration and state currently lives under a hardcoded
/etc/pihole. Packagers that install FTL into a different prefix (snaps, /opt
installs, read-only-root images, ...) have to patch the sources.

Introduce a single compile-time base directory, PIHOLE_INSTALL_DIR, defaulting
to /etc/pihole so behaviour is unchanged, and route the /etc/pihole paths
through it: the hardcoded config/state defines (pihole.toml, the legacy config,
dnsmasq.conf, hosts, custom.list, dhcp.leases, backups, cli_pw, versions, the
inotify watch dir), the embedded dnsmasq CONFFILE, and the /etc/pihole defaults
of the runtime-settable config options (files.database, files.tmp_db,
files.gravity, files.macvendor, webserver.tls.cert). Packagers can now relocate
the tree with

    cmake -DPIHOLE_INSTALL_DIR=/opt/pihole/etc ...

This is a build-time knob only and deliberately does not add a runtime override
(files.pid stays fixed per GHSA-6w8x-p785-6pm4). The /var/log/pihole log
defaults are left untouched as they are a different base directory.

Teleporter archives stay portable across installs, so the archive entry names
for pihole.toml and dhcp.leases remain the canonical etc/pihole/... ones
regardless of PIHOLE_INSTALL_DIR - only the on-disk source path is relocated.
Export previously derived both names from the absolute path minus its leading
slash, which would have made a relocated build write opt/pihole/etc/... while
the import side still matched the fixed names, silently skipping config and
lease import. Both sides now share ZIPNAME_TOML/ZIPNAME_DHCPLEASES, and a test
asserts the exported archive uses those names.

The embedded OpenAPI specs are run through configure_file() so the five
relocatable defaults they document (cert, database, tmp_db, gravity, macvendor)
and the two adlists.list mentions in lists.yaml follow the configured value
instead of documenting paths a relocated install does not use.

CMake rejects a relative or trailing-slash PIHOLE_INSTALL_DIR, as the teleporter
strips the leading slash by offset.

Signed-off-by: Boris Rybalkin <support@syncloud.it>
@cyberb
cyberb force-pushed the pr/configurable-install-dir branch from 10de901 to 91d220e Compare August 24, 2026 20:18
@cyberb

cyberb commented Aug 24, 2026

Copy link
Copy Markdown
Author

Rebased onto current development.

Teleporter: took the fixed-name option — pihole.toml and dhcp.leases keep the canonical etc/pihole/... entry names regardless of PIHOLE_INSTALL_DIR, so archives stay portable and existing ones still import. Export and import now share the same two defines. Scope left as-is, no FHS split.

All five minor items done: specs (config.yaml + lists.yaml) are substituted at build time via configure_file, dnsmasq_config.c uses GLOBALTOMLPATH, args.c help uses PIHOLE_INSTALL_DIR, header comment fixed, and CMake now rejects a relative or trailing-slash value. Added a test asserting the exported archive uses the canonical names.

Kept the PIHOLE_INSTALL_DIR name — happy to rename if you prefer PIHOLE_CONFIG_DIR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants