Installing Cloudlog on OpenBSD¶
This page installs Cloudlog on OpenBSD with the base web server httpd, MariaDB, and PHP 8.5 from packages. Commands use doas.
PHP 8.5 packages are in OpenBSD 8.0 and newer. On an older release, upgrade OpenBSD first. The ports tree ships php-8.5, and pkg_add -u is how you pick up 8.5.12 and later once the OpenBSD packages have been rebuilt. 8.5.12 is the release that fixes the PHP2Shell issues (CVE-2026-109618 and CVE-2026-109619).
Use a 64-bit machine. Microwave QSOs need it, because the frequencies do not fit in a 32-bit integer.
If you would prefer someone else to run the server, magicbug offer hosted Cloudlog.
After each pkg_add, read the file it points you at under /usr/local/share/doc/pkg-readmes/. Those notes are the ones that match the packages you just installed, and they win if a path in this page has moved.
What you need¶
- OpenBSD 8.0 or newer.
httpdfrom the base system. It runs chrooted in/var/www.- MariaDB from packages.
- PHP 8.5 with the curl, mysqli, gd, and zip extensions. mbstring, xml, openssl, and json are part of the main PHP build. Confirm with
php-8.5 -mafter install.
allow_url_fopen needs to be on. Country files, LoTW user data, and several award lists are fetched that way.
1. Install PHP 8.5¶
doas pkg_add php%8.5 php-curl%8.5 php-mysqli%8.5 php-gd%8.5 php-zip%8.5
If pkg_add asks which PHP you want, choose 8.5. Every extension has to be the 8.5 build. Mixing 8.3 and 8.5 modules will not load.
If php-8.5 -m does not list mbstring, add it:
doas pkg_add php-mbstring%8.5
Enable the extensions by linking their ini files into /etc/php-8.5/:
doas mkdir -p /etc/php-8.5
for module in curl mysqli gd zip mbstring; do
if [ -f "/etc/php-8.5.sample/${module}.ini" ]; then
doas ln -sf "../php-8.5.sample/${module}.ini" "/etc/php-8.5/${module}.ini"
fi
done
Edit /etc/php-8.5.ini:
date.timezone = Europe/London
memory_limit = 256M
upload_max_filesize = 32M
post_max_size = 40M
max_execution_time = 300
allow_url_fopen = On
Use your own timezone. The list is on php.net. post_max_size needs to be larger than upload_max_filesize.
Check the version. The binary is php-8.5 until something else provides a plain php:
php-8.5 -v
php-8.5 -m | grep -iE 'curl|mysqli|mbstring|xml|openssl|zip|gd|json'
You want 8.5.12 or later. If the package is still the previous 8.5 build, run doas pkg_add -u again after the OpenBSD mirrors have caught up, and do not leave the older build facing the internet.
2. PHP-FPM¶
The package readme for php-8.5 names the rc script. On current packages it is php85_fpm.
httpd looks for the socket inside its chroot, so the listen path on disk is /var/www/run/php-fpm.sock and the path in httpd.conf is /run/php-fpm.sock.
Edit /etc/php-fpm.conf and set the www pool along these lines. Keep the process limits the file already has:
[www]
user = www
group = www
listen = /var/www/run/php-fpm.sock
listen.owner = www
listen.group = www
listen.mode = 0660
Look for a chroot line in that pool.
- If
chroot = /var/wwwis set, PHP can only see files under/var/www. Cloudlog's folder has to live there, which this page does. MariaDB's socket will not be visible, so the install wizard hostname must be127.0.0.1. Copy the name service files into the jail, in section 5. - If
chrootis empty, PHP can see the rest of the disk, and the wizard hostname can belocalhost.
doas php-fpm-8.5 -t
doas rcctl enable php85_fpm
doas rcctl start php85_fpm
doas rcctl check php85_fpm
If rcctl does not know php85_fpm, the package readme has the script name that was installed.
3. httpd¶
httpd does not read .htaccess. Paths in httpd.conf are relative to /var/www.
Cloudlog links look like /index.php/lotw/load_users. A location that only matches a name ending in .php skips those, and the wizard and the cron jobs fail. Use both locations.
Create /etc/httpd.conf:
types { include "/usr/share/misc/mime.types" }
server "cloudlog.example.com" {
listen on * port 80
root "/htdocs/cloudlog"
directory index index.php
location "/application/*" { block }
location "/system/*" { block }
location "/backup/*" { block }
location "/updates/*" { block }
location "/.git*" { block }
location "*.php" {
fastcgi socket "/run/php-fpm.sock"
}
location "*.php/*" {
fastcgi socket "/run/php-fpm.sock"
}
}
Replace cloudlog.example.com with the name you will use. block returns 403 for the folders that hold config, logs, and backups. PHP still reads them from disk.
doas rcctl enable httpd
doas rcctl start httpd
doas rcctl check httpd
For a public site, add TLS with httpd and a real certificate before you log in for normal use. When you do, set the Website URL in the wizard (or base_url in application/config/config.php) to the https:// address, and set $config['cookie_secure'] = TRUE; so the login cookie is only sent over HTTPS.
4. MariaDB¶
doas pkg_add mariadb-server mariadb-client
Follow /usr/local/share/doc/pkg-readmes/mariadb-server. The usual sequence, when the readme agrees, is:
doas mariadb-install-db
doas rcctl enable mariadb
doas rcctl start mariadb
doas mysql_secure_installation
Some package versions still call the rc script mysqld and the bootstrap tool mysql_install_db. Use the names the readme prints.
Then:
doas mysql -u root -p
CREATE DATABASE cloudlog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'cloudlog'@'localhost' IDENTIFIED BY 'replace-this-with-a-long-password';
GRANT ALL PRIVILEGES ON cloudlog.* TO 'cloudlog'@'localhost';
FLUSH PRIVILEGES;
QUIT
If PHP-FPM is chrooted, also allow the same user over TCP, because PHP will connect to 127.0.0.1:
CREATE USER 'cloudlog'@'127.0.0.1' IDENTIFIED BY 'replace-this-with-a-long-password';
GRANT ALL PRIVILEGES ON cloudlog.* TO 'cloudlog'@'127.0.0.1';
FLUSH PRIVILEGES;
5. Files the chroot needs¶
Do this when PHP-FPM has chroot = /var/www, and also if country-file downloads fail with a certificate or DNS error. httpd itself is always chrooted. Outbound HTTPS from PHP needs a resolver and the CA bundle inside the jail when PHP is chrooted too.
doas mkdir -p /var/www/etc/ssl /var/www/tmp
doas cp /etc/ssl/cert.pem /var/www/etc/ssl/
doas cp /etc/resolv.conf /etc/hosts /etc/services /var/www/etc/
doas chown www:www /var/www/tmp
doas chmod 1777 /var/www/tmp
Copy resolv.conf again if you change DNS servers. The jail has its own copy.
6. Download Cloudlog and set the folders¶
doas pkg_add git
doas mkdir -p /var/www/htdocs
doas git clone https://github.com/magicbug/Cloudlog.git /var/www/htdocs/cloudlog
That checks out master, which is the branch to run. On disk the site is /var/www/htdocs/cloudlog. Inside the httpd chroot that is /htdocs/cloudlog, which matches root in section 3.
PHP runs as www. These folders have to be writable by that group:
application/config/application/logs/application/cache/application/plugins/backup/updates/uploads/images/eqsl_card_images/assets/qslcard/assets/json/assets/sstvimages/
Warning: this gets Cloudlog running. application/config will contain the database password, and backup will contain a copy of the log. The location blocks in section 3 stop visitors fetching them.
CLOUDLOG=/var/www/htdocs/cloudlog
doas chown -R root:www "$CLOUDLOG"
for dir in \
application/config \
application/logs \
application/cache \
application/plugins \
backup \
updates \
uploads \
images/eqsl_card_images \
assets/qslcard \
assets/json \
assets/sstvimages
do
doas mkdir -p "$CLOUDLOG/$dir"
doas chown -R root:www "$CLOUDLOG/$dir"
doas chmod -R g+rwX "$CLOUDLOG/$dir"
done
7. Run the install wizard¶
Open http://cloudlog.example.com/install.
- Directory: leave this empty.
httpd's root is the Cloudlog folder. - Website URL: the address you use in the browser.
- Default gridsquare: your locator, for example
IO91JS. - Hostname:
localhost, or127.0.0.1when PHP-FPM is chrooted to/var/www. - Username, password, database name: the values from section 4.
The wizard writes the config files, loads the database, tries to pull the country file, and then deletes the install folder. If that folder is still there:
doas rm -rf /var/www/htdocs/cloudlog/install
The wizard marks the two config files world writable. Put them back:
doas chown root:www /var/www/htdocs/cloudlog/application/config/config.php \
/var/www/htdocs/cloudlog/application/config/database.php
doas chmod 640 /var/www/htdocs/cloudlog/application/config/config.php \
/var/www/htdocs/cloudlog/application/config/database.php
If the wizard says the config folder is not writable, section 6 has not been applied, or PHP-FPM is not running as www. The wizard mentions chmod 777. You do not need that.
httpd will not honour .htaccess, so leave $config['index_page'] as index.php. The cron jobs use that form.
8. First login¶
Log in with username m0abc and password demo.
Then:
- Admin menu, User Accounts: create yourself as an Administrator, log in as that user, and delete
m0abc. - Admin menu, Update Country Files. The dashboard warns when the country list is empty.
- Callsign menu (top right), Station Locations: create a station location and set it active.
- Callsign menu, Station Logbooks: create a logbook and attach the station location.
- Admin menu, Debug Information: confirm PHP is 8.5.12 or later, the modules are installed, and
backup,updates, anduploadsare writable.
Callsign menu, Account, third party logins, for QRZ or HamQTH lookups. Uploading QSOs uses the station location: the QRZ logbook API key, Club Log with Upload to Clublog set to Yes (Club Log wants an email address), the HRDLog username and code, eQSL mappings, or a LoTW certificate.
Database migrations run when a page loads. After a git pull, open the site once.
9. Updates and cron¶
Save this as /usr/local/sbin/cloudlog.sh:
#!/bin/sh
CLOUDLOG=/var/www/htdocs/cloudlog
URL=https://cloudlog.example.com
cd "$CLOUDLOG" || exit 1
/usr/local/bin/git pull
for dir in \
application/config \
application/logs \
application/cache \
application/plugins \
backup \
updates \
uploads \
images/eqsl_card_images \
assets/qslcard \
assets/json \
assets/sstvimages
do
mkdir -p "$CLOUDLOG/$dir"
chown -R root:www "$CLOUDLOG/$dir"
chmod -R g+rwX "$CLOUDLOG/$dir"
done
chmod 640 "$CLOUDLOG/application/config/config.php" \
"$CLOUDLOG/application/config/database.php"
# Copy a fresh resolver into the jail when PHP-FPM is chrooted.
if [ -d /var/www/etc ]; then
cp /etc/resolv.conf /var/www/etc/resolv.conf
fi
/usr/local/bin/curl --silent "$URL/index.php/user/login" >/dev/null 2>&1 || true
doas chmod 700 /usr/local/sbin/cloudlog.sh
If git refuses because of the directory owner, as the user that will run the script:
git config --global --add safe.directory /var/www/htdocs/cloudlog
doas crontab -e. OpenBSD cron does not have /usr/local/bin on its path. Skip any service you have not configured.
0 0 * * * /bin/sh /usr/local/sbin/cloudlog.sh
# Club Log. Needs the account login, and Upload to Clublog on a station location.
0 */6 * * * /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/clublog/upload >/dev/null 2>&1
# LoTW upload, which also downloads matching QSOs. Needs a certificate on the station.
0 * * * * /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/lotw/lotw_upload >/dev/null 2>&1
# HRDLog. Needs the username and code on the station location.
0 */6 * * * /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/hrdlog/upload >/dev/null 2>&1
# QRZ logbook. Needs the API key on the station location.
0 */6 * * * /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/qrz/upload >/dev/null 2>&1
# eQSL upload and download. See the eQSL wiki page for mappings.
9 */6 * * * /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/eqsl/sync >/dev/null 2>&1
# Reference data
@weekly /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/lotw/load_users >/dev/null 2>&1
@weekly /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/update/update_clublog_scp >/dev/null 2>&1
@monthly /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/update/update_dok >/dev/null 2>&1
@monthly /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/update/update_sota >/dev/null 2>&1
@monthly /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/update/update_wwff >/dev/null 2>&1
@monthly /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/update/update_pota >/dev/null 2>&1
# Plugins. Skip this when you have none that use cron.
0 * * * * /usr/local/bin/curl --silent https://cloudlog.example.com/index.php/plugins/cron >/dev/null 2>&1
Run the LoTW user load once in a browser: https://cloudlog.example.com/index.php/lotw/load_users. You should see "LoTW User Data Saved."
When something is wrong¶
| What you see | What to check |
|---|---|
| Wizard says the config folder is not writable | Section 6. PHP-FPM's pool user is www. |
| "The database tables could not be created" | Hostname is 127.0.0.1 when PHP is chrooted, and that user was granted access. |
| "Directory cannot be found" | Directory on the wizard is empty, and root in httpd.conf is /htdocs/cloudlog. |
| The wizard or cron URLs 404 | The *.php/* location is present, and php85_fpm is running. doas rcctl check php85_fpm. |
| Country files fail with a certificate or DNS error | Section 5. allow_url_fopen is on in /etc/php-8.5.ini. Restart php85_fpm after editing it. |
| Debug page shows an older PHP | doas pkg_add -u, then restart php85_fpm. |
Server setup questions belong on the discussions board. Bugs in Cloudlog itself belong in GitHub issues.