Loghound installs to /opt/loghound by default and every generated artefact follows the prefix. The file modes are what decide whether the panel works at all, so they are verified rather than assumed.
sudo ./install/install.sh --prefix=/var/www/loghound
Everything follows: the vhost DocumentRoot, the pool’s open_basedir and session.save_path, the systemd WorkingDirectory, ExecStart and ReadWritePaths, the config file path, the deny rules and the paths in the final summary. The installer greps its own generated files afterwards to prove nothing kept the default, and aborts if anything did.
A concrete reason to move it: some server-management platforms auto-discover applications by scanning subdirectories of a base directory, and an application that lives there gets git deploy, backups, restore, vhost and FPM management and log viewing from that platform’s interface for free.
| Path | Mode | Owner | Why |
|---|---|---|---|
<prefix> | 0751 | root:loghound | The web server user must traverse it to reach public/. It must not be able to list it. |
public | 0755 | root:loghound | World readable, and it holds no secrets. The web server serves b.js and the assets directly, as itself. |
config | 0700 | loghound:loghound | The API key and the beacon signing secret. The web server user must not be able to read this. |
var | 0750 | loghound:loghound | SQLite state, the status file, the bad-line sample. |
var/sessions | 0700 | loghound:loghound | A PHP session file is a signed-in panel session. |
src, bin, solr | 0750 | root:loghound | Code is root-owned and only read by the service user: a compromise of the daemon must not be able to rewrite the code that runs next time. |
Verify it yourself, as the users themselves. The third line is the one that matters most, and it is supposed to fail:
sudo -u www-data test -x /opt/loghound && echo "traverse ok" sudo -u www-data test -r /opt/loghound/public/index.php && echo "docroot ok" sudo -u www-data test -r /opt/loghound/config && echo "SECRETS EXPOSED" || echo "config protected ok" sudo -u loghound test -r /var/log/apache2/access.log && echo "logs readable"
Figure 1 — who may do what. The one crossing that must fail is the web server user reading the configuration directory, and the installer verifies it with an inverted check rather than assuming it.
If the prefix is 0750, Apache fails with:
AH00035: access denied because search permissions are missing on a component of the path /opt/loghound/public/index.php
loghound group
It is the tempting fix and it is wrong on a live box. Supplementary groups are read once, at process start, so it needs a full Apache restart rather than a reload — and a restart drops in-flight requests on every other site on the machine. Grant the traversal by mode instead. That is what 0751 is for.
open_basedirThe panel runs in its own PHP-FPM pool as the service user, with file_uploads = off and a disable_functions list covering every process-spawning function. Its open_basedir is wider than the application directory on purpose:
php_admin_value[open_basedir] = /opt/loghound:/var/log:/etc/apache2:/etc/nginx:/tmp
The last three are there because the browser installer’s first screen reads them: it parses your web server configuration to find the access logs and their exact format, and shows you real sample lines so you can confirm the mapping before anything is ingested. Leave them out and setup still completes, but that screen finds nothing and you type paths by hand.
open_basedir widens what PHP may attempt, not what the kernel will allow
Both directories stay read-only to the pool user by file permissions. Drop whichever web server you do not run; the comment in install/php-fpm-pool.conf.example says which line. The ingest daemon is never affected either way — it is a CLI process and the restriction does not apply to it.