Nextcloud migration: Linux to FreeBSD (Bastille jail build log)
Overview
This document is the exact build log for bringing up a production-grade Nextcloud instance inside a Bastille jail on TrashProBSD (FreeBSD 14.3-RELEASE) using:
- nginx
- php-fpm (PHP 8.2)
- PostgreSQL 17
- Redis
- APCu
This reflects the real environment now serving Nextcloud behind a reverse proxy, with ZFS snapshots for rollback.
Environment
- Jail:
cloud - Release:
14.3-RELEASE - Jail IP:
192.168.0.230 - Host interface:
bge1 - vnet MAC:
25:7c:58:9e:9d:40 - Web stack: nginx + php-fpm on
127.0.0.1:9000 - Database: PostgreSQL 17
- Cache/locking: Redis + APCu
0) Jail creation and vnet MAC (Bastille)
Create the Bastille jail with vnet networking and pin a stable MAC for DHCP/ARP consistency.
bastille create -V cloud 14.3-RELEASE 0.0.0.0 bge1
Bastille handled all vnet/epair wiring automatically; the only manual networking change was pinning a static MAC on the jail-side interface.
Config: /usr/local/bastille/jails/cloud/jail.conf
Minimal jail.conf (vnet + MAC + IP)
This is the actual /usr/local/bastille/jails/cloud/jail.conf. Bastille manages the vnet/epair plumbing; the only manual change is the pinned jail-side MAC.
cloud {
enforce_statfs = 2;
devfs_ruleset = 13;
exec.clean;
exec.consolelog = /var/log/bastille/cloud_console.log;
exec.start = '/bin/sh /etc/rc';
exec.stop = '/bin/sh /etc/rc.shutdown';
host.hostname = cloud;
mount.devfs;
mount.fstab = /usr/local/bastille/jails/cloud/fstab;
path = /usr/local/bastille/jails/cloud/root;
securelevel = 2;
osrelease = 14.3-RELEASE;
allow.raw_sockets = 1;
allow.sysvipc = 1;
vnet;
vnet.interface = e0b_cloud;
exec.prestart += "jib addm cloud bge1";
exec.prestart += "ifconfig e0a_cloud description \"vnet0 host interface for Bastille jail cloud\"";
exec.prestart += "ifconfig e0b_cloud ether 25:7c:58:9e:9d:40";
exec.poststop += "ifconfig e0a_cloud destroy";
}
Notes:
e0a_cloud(host) /e0b_cloud(jail) are Bastille-managed epairs.jib addm cloud bge1attaches the host-side epair tobge1.- The MAC on
e0b_cloudis intentionally pinned for DHCP/ARP stability.
1) Packages (nginx + PHP 8.2 + modules)
Install the full PHP 8.2 runtime and extensions required by Nextcloud (image handling, DB, intl, caching).
pkg install -y \
nginx \
php82 php82-extensions \
php82-gd php82-curl php82-mbstring php82-xml php82-zip php82-intl \
php82-bcmath php82-fileinfo php82-pecl-imagick php82-opcache \
php82-pdo php82-pdo_pgsql php82-pgsql
Additional PHP modules installed later (Nextcloud feature deps)
These were missing at first and were installed later as Nextcloud’s checks/features complained (LDAP, mail, EXIF, etc.). Keeping this list here prevents the “why is this feature broken” loop later.
pkg install -y \
php82-ldap \
php82-imap \
php82-exif \
php82-gmp \
php82-bz2 \
php82-ftp \
php82-sockets
service php_fpm restart
2) php-fpm
Enable and start PHP-FPM, which nginx proxies to on 127.0.0.1:9000.
sysrc php_fpm_enable="YES"
service php_fpm start
sockstat -4l | egrep ':9000'
3) nginx
Enable nginx and validate the base configuration before adding the Nextcloud vhost.
sysrc nginx_enable="YES"
service nginx start
nginx -t
4) nginx vhost for Nextcloud
Define the PHP-enabled vhost for Nextcloud; TLS termination is handled by the upstream reverse proxy.
server {
listen 80 default_server;
server_name cloud.example.com;
root /usr/local/www/nextcloud;
index index.php index.html;
client_max_body_size 10G;
location / {
try_files $uri $uri/ /index.php$request_uri;
}
location ~ \.php(?:$|/) {
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}
}
5) PostgreSQL 17 + Redis
Bring up the database and Redis used by Nextcloud for metadata, sessions, and locking.
pkg install -y postgresql17-server postgresql17-client redis
sysrc redis_enable="YES"
service redis start
sysrc postgresql_enable="YES"
service postgresql initdb
service postgresql start
su -m postgres -c "createuser -P nextcloud"
su -m postgres -c "createdb -O nextcloud nextcloud"
6) Nextcloud files + data dir
Install Nextcloud under the web root and place user data outside the document root.
cd /usr/local/www
fetch -o nextcloud.tar.bz2 https://download.nextcloud.com/server/releases/latest.tar.bz2
tar -xjf nextcloud.tar.bz2
mkdir -p /usr/local/www/nextcloud-data
chown -R www:www /usr/local/www/nextcloud /usr/local/www/nextcloud-data
chmod 750 /usr/local/www/nextcloud-data
7) Nextcloud install (occ)
Perform a non-interactive Nextcloud install wired to PostgreSQL and the external data directory.
su -m www -c 'cd /usr/local/www/nextcloud && php occ maintenance:install \
--database "pgsql" \
--database-host "127.0.0.1" \
--database-name "nextcloud" \
--database-user "nextcloud" \
--database-pass "DB_PASSWORD_HERE" \
--admin-user "admin" \
--admin-pass "ADMIN_PASSWORD_HERE" \
--data-dir "/usr/local/www/nextcloud-data"'
8) APCu + Redis caching
Enable local memory caching (APCu) and Redis locking to reduce DB contention.
pkg install -y php82-pecl-APCu php82-pecl-redis
cd /usr/local/etc/php
cp -n ext-20-APCu.ini.sample ext-20-APCu.ini
cp -n ext-30-redis.ini.sample ext-30-redis.ini
service php_fpm restart
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu"'
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set memcache.locking --value="\\OC\\Memcache\\Redis"'
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set redis host --value="127.0.0.1"'
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set redis port --value="6379" --type=integer'
9) Cron jobs
Move Nextcloud background jobs to system cron for reliability and performance.
su -m www -c 'php /usr/local/www/nextcloud/occ background:cron'
cat > /etc/cron.d/nextcloud <<'EOF'
*/5 * * * * www /usr/local/bin/php -f /usr/local/www/nextcloud/cron.php
EOF
10) Domains, proxy trust, canonical URLs
Configure trusted domains and reverse-proxy awareness for external access.
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set trusted_domains 1 --value="192.168.0.230"'
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set trusted_domains 2 --value="cloud.comtoso.com"'
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set trusted_domains 3 --value="icloud.comtoso.com"'
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set trusted_domains 4 --value="nextcloud.comtoso.com"'
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set trusted_proxies 0 --value="192.168.0.218"'
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set overwritehost --value="icloud.comtoso.com"'
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set overwrite.cli.url --value="https://nextcloud.comtoso.com"'
su -m www -c 'php /usr/local/www/nextcloud/occ config:system:set overwriteprotocol --value="https"'
11) ZFS snapshots
Create a rollback point before risky changes or migrations.
TAG="manual-pre-change-$(date +%F-%H%M)"
zfs snapshot -r zroot/bastille/jails/cloud@"$TAG"
Rollback:
bastille stop cloud
zfs rollback -r zroot/bastille/jails/cloud@manual-pre-change-YYYY-MM-DD-HHMM
bastille start cloud
Fixes / tuning applied after first run
These were required to clear Nextcloud warnings and stabilize performance under real load.
PHP memory limits (uploads, previews, large sync jobs)
# /usr/local/etc/php.ini
memory_limit = 1024M
upload_max_filesize = 10G
post_max_size = 10G
max_execution_time = 3600
max_input_time = 3600
service php_fpm restart
OPcache tuning (interned strings exhaustion)
# /usr/local/etc/php/ext-20-opcache.ini
opcache.enable=1
opcache.memory_consumption=256
opcache.interned_strings_buffer=32
opcache.max_accelerated_files=20000
opcache.revalidate_freq=60
service php_fpm restart
PHP getenv() / PATH visibility (Nextcloud warning)
Ensure php-fpm inherits a sane PATH so Nextcloud environment checks pass:
# /usr/local/etc/php-fpm.d/www.conf
env[PATH] = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
service php_fpm restart
Optional: Redis persistence hardening
# /usr/local/etc/redis.conf
appendonly yes
appendfsync everysec
service redis restart
State of the Build
At this point nginx + php-fpm serve Nextcloud, PostgreSQL 17 is live, Redis + APCu are active, cron runs, HTTPS proxying works, and ZFS snapshots protect the jail. Next step is cold migration from TrueNAS into this jail.