Documentation
OpenBSD Deployment
Manual OpenBSD deployment notes for packages, filesystem layout, slowcgi, httpd, GeoIP, shop, and redirects.
OPENBSD_DEPLOY.mdFor a fresh OpenBSD VPS, use the interactive installer first:
perl install/openbsd-install.pl --dry-run --domain desertarchive.kldhosting.com
doas perl install/openbsd-install.pl
See OPENBSD_INSTALL.md for the full dry-run, server-admin, DNS, pf, httpd, acme-client, local-asset, and filesystem layout walkthrough. The older VULTR_OPENBSD_INSTALL.md remains as a Vultr-specific reference.
These examples assume:
- Site domain:
example.com - Shop module path:
/shop - App code:
/usr/local/www/desertcms - Config:
/etc/desertcms.conf - Data:
/var/desertcms - Public output:
/var/www/htdocs/desert-archive - Editable themes:
/var/desertcms/themes
Packages
pkg_add p5-DBI p5-DBD-SQLite libvips p5-HTTP-Daemon
Firewall
The installer writes a default-deny /etc/pf.conf based on etc/pf.conf.example.
Manual validation:
pfctl -nf /etc/pf.conf
pfctl -f /etc/pf.conf
pfctl -e
Only SSH from the configured admin CIDR and public TCP 80/443 should be passed.
User And Directories
useradd -s /sbin/nologin -d /var/empty _desertcms
install -d -o root -g wheel -m 755 /usr/local/www/desertcms
install -d -o _desertcms -g _desertcms -m 750 /var/desertcms
install -d -o _desertcms -g _desertcms -m 750 /var/desertcms/backups
install -d -o _desertcms -g _desertcms -m 750 /var/desertcms/originals
install -d -o _desertcms -g _desertcms -m 750 /var/desertcms/themes
install -d -o _desertcms -g _desertcms -m 755 /var/www/htdocs/desert-archive
install -d -o www -g www -m 755 /var/www/run
Copy this repository to /usr/local/www/desertcms, then:
cp /usr/local/www/desertcms/etc/desertcms.conf.example /etc/desertcms.conf
chown root:_desertcms /etc/desertcms.conf
chmod 640 /etc/desertcms.conf
Initialize
cd /usr/local/www/desertcms
DESERTCMS_CONFIG=/etc/desertcms.conf perl bin/desertcms-maint.pl init-db
DESERTCMS_CONFIG=/etc/desertcms.conf perl bin/desertcms-maint.pl create-admin setup-admin
init-db also seeds the default editable theme into /var/desertcms/themes/default. When create-admin is run without --password, it prints a temporary password and forces the first CMS login to set the permanent username and password.
The CMS keeps only one active admin account. Use create-admin only for first initialization. For production handoff or recovery, use:
DESERTCMS_CONFIG=/etc/desertcms.conf perl bin/desertcms-maint.pl reset-admin setup-admin
That resets the single active admin, revokes existing sessions, and forces the next login to choose permanent credentials.
Local Asset Audit
Runtime admin and theme assets are local. Before deployment:
perl tools/check-local-assets.pl
The interactive installer runs this automatically before copying the application.
slowcgi
Copy etc/rc.d/desertcms_slowcgi to /etc/rc.d/desertcms_slowcgi, then:
chmod 555 /etc/rc.d/desertcms_slowcgi
rcctl enable desertcms_slowcgi
rcctl start desertcms_slowcgi
The example runs slowcgi with -p / so the CGI script can live outside the httpd chroot while still communicating through /var/www/run/desertcms.sock.
httpd
Merge etc/httpd.conf.example into /etc/httpd.conf, adjust the domain, issue the certificate with acme-client, then:
acme-client -v example.com
httpd -n
rcctl reload httpd
Open:
https://example.com/admin/login
The example also forwards /analytics*, /comments*, and /ratings* to the same Perl CGI app. Public pages remain static files, while analytics collection, post comment threads, and post ratings use those small dynamic endpoints.
For visitor country, region, and city data, import a local GeoIP city database after the CMS is installed. Runtime analytics collection reads the local SQLite GeoIP range table and does not send visitor IPs to an external lookup service.
For a launch-ready free DB-IP City Lite import on a small VPS, use the observed-range mode. It downloads the monthly DB-IP City Lite file, imports only the ranges matching visitor IPs already present in analytics, and backfills those rows:
doas su -m _desertcms -c 'env DESERTCMS_CONFIG=/etc/desertcms.conf perl /usr/local/www/desertcms/bin/desertcms-maint.pl geoip-refresh-dbip-lite --observed-only'
The full DB-IP city file contains millions of ranges and can be too heavy for small VPS deployments. Use the full import only when the server has enough CPU, memory, and disk for the complete table:
doas su -m _desertcms -c 'env DESERTCMS_CONFIG=/etc/desertcms.conf perl /usr/local/www/desertcms/bin/desertcms-maint.pl geoip-refresh-dbip-lite'
DB-IP Lite requires attribution where results are displayed; DesertCMS adds that attribution on the admin dashboard when DB-IP data is active.
For a simple CSV or TSV with network,country,region,city headers:
doas su -m _desertcms -c 'env DESERTCMS_CONFIG=/etc/desertcms.conf perl /usr/local/www/desertcms/bin/desertcms-maint.pl geoip-import /var/desertcms/geoip.tsv'
doas su -m _desertcms -c 'env DESERTCMS_CONFIG=/etc/desertcms.conf perl /usr/local/www/desertcms/bin/desertcms-maint.pl geoip-backfill'
For GeoLite2-style city CSV exports:
doas su -m _desertcms -c 'env DESERTCMS_CONFIG=/etc/desertcms.conf perl /usr/local/www/desertcms/bin/desertcms-maint.pl geoip-import --blocks /var/desertcms/GeoLite2-City-Blocks-IPv4.csv --locations /var/desertcms/GeoLite2-City-Locations-en.csv'
doas su -m _desertcms -c 'env DESERTCMS_CONFIG=/etc/desertcms.conf perl /usr/local/www/desertcms/bin/desertcms-maint.pl geoip-import --append --blocks /var/desertcms/GeoLite2-City-Blocks-IPv6.csv --locations /var/desertcms/GeoLite2-City-Locations-en.csv'
doas su -m _desertcms -c 'env DESERTCMS_CONFIG=/etc/desertcms.conf perl /usr/local/www/desertcms/bin/desertcms-maint.pl geoip-backfill'
Repeat the import periodically when the local GeoIP data is refreshed. The first import replaces the local table; subsequent --append imports add ranges without clearing the table. IPv4 and IPv6 CIDR ranges are supported by the simple importer.
The Shop module forwards /shop* routes to the same CGI app with /etc/desertcms.conf: /shop, /shop/checkout, /shop/success, /shop/cancel, and /shop/stripe/webhook. Static /assets/... files remain served from the main public webroot. Configure Stripe secrets in /etc/desertcms.conf and point the Stripe webhook endpoint at:
https://example.com/shop/stripe/webhook
Keep the HTTPS server block's connection max request body 67108864 line. OpenBSD httpd defaults to 1 MB, so omitting that line causes photo uploads to fail with 413 Payload Too Large. The CMS stores full-resolution originals privately and creates downscaled public display derivatives.
Redirect Rules
The admin Redirects screen writes redirects.httpd.conf into the generated public root. Review that file after publishing and copy its location blocks into the TLS server block in /etc/httpd.conf, then run:
httpd -n
rcctl reload httpd
The CMS also writes static redirect fallback pages so local development and simple static serving still point old URLs at their new targets.