This post builds on top of the previous post in which we’ve created a simple thin Jail. The goal of this post is, to put a bit of automation around this and to provide a simple PostgreSQL as a service platform. We want to do this by creating one PostgreSQL instance per Jail (without any restrictions on resource usage for now, no monitoring and so on). Before we start with this, let’s remove the sample thinjail1 we’ve created in the last post:
root@freebsd14:~ $ jls
JID IP Address Hostname Path
1 192.168.122.160 thinjail1 /usr/local/jails/containers/thinjail1
root@freebsd14:~ $ service jail stop thinjail1
Stopping jails: thinjail1.
root@freebsd14:~ $ chflags -R 0 /usr/local/jails/containers/thinjail1
root@freebsd14:~ $ rm -rf /usr/local/jails/containers/thinjail1/*
root@freebsd14:~ $ jls
JID IP Address Hostname Path
root@freebsd14:~ $ zfs destroy zroot/jails/containers/thinjail1
We’ll also remove the ZFS snapshot because we’ll be creating a new one containing additional packages and configuration:
root@freebsd14:~ $ zfs destroy zroot/jails/templates/14.1-RELEASE@base
root@freebsd14:~ $ zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zroot/ROOT/default@2024-11-26-10:56:43-0 394M - 1.46G -
zroot/ROOT/default@2024-12-04-08:41:41-0 2.23M - 2.49G -
zroot/ROOT/default@2024-12-04-08:42:46-0 1.86M - 2.58G -
Remember that this was our base for the Jail:
root@freebsd14:~ $ ls -al /usr/local/jails/templates/14.1-RELEASE/
total 95
drwxr-xr-x 18 root wheel 22 Dec 2 13:35 .
drwxr-xr-x 3 root wheel 3 Dec 2 13:29 ..
-rw-r--r-- 2 root wheel 1011 May 31 2024 .cshrc
-rw-r--r-- 2 root wheel 495 May 31 2024 .profile
-r--r--r-- 1 root wheel 6109 May 31 2024 COPYRIGHT
drwxr-xr-x 2 root wheel 49 Dec 2 13:40 bin
drwxr-xr-x 15 root wheel 69 Dec 2 13:40 boot
dr-xr-xr-x 2 root wheel 2 May 31 2024 dev
drwxr-xr-x 30 root wheel 105 Dec 2 13:40 etc
drwxr-xr-x 4 root wheel 78 Dec 2 13:40 lib
drwxr-xr-x 3 root wheel 5 May 31 2024 libexec
drwxr-xr-x 2 root wheel 2 May 31 2024 media
drwxr-xr-x 2 root wheel 2 May 31 2024 mnt
drwxr-xr-x 2 root wheel 2 May 31 2024 net
dr-xr-xr-x 2 root wheel 2 May 31 2024 proc
drwxr-xr-x 2 root wheel 150 Dec 2 13:40 rescue
drwxr-x--- 2 root wheel 7 May 31 2024 root
drwxr-xr-x 2 root wheel 150 Dec 2 13:40 sbin
lrwxr-xr-x 1 root wheel 11 May 31 2024 sys -> usr/src/sys
drwxrwxrwt 2 root wheel 2 May 31 2024 tmp
drwxr-xr-x 14 root wheel 14 May 31 2024 usr
drwxr-xr-x 24 root wheel 24 May 31 2024 var
What we’re doing now is to install PostgreSQL into this base so we’ll have that available immediately when we create new Jails afterwards. To do this we can ask pkg to perform it’s actions against a chroot environment instead of the host system by using the “-c” switch:
root@freebsd14:~ $ pkg -c /usr/local/jails/templates/14.1-RELEASE/ install postgresql17-server-17.2 postgresql17-contrib-17.2
Updating FreeBSD repository catalogue...
FreeBSD repository is up to date.
All repositories are up to date.
The following 17 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
gettext-runtime: 0.22.5
icu: 74.2_1,1
indexinfo: 0.3.1
libedit: 3.1.20240808,1
libffi: 3.4.6
liblz4: 1.10.0,1
libxml2: 2.11.9
llvm15: 15.0.7_10
lua53: 5.3.6_1
mpdecimal: 4.0.0
perl5: 5.36.3_2
postgresql17-client: 17.2
postgresql17-contrib: 17.2
postgresql17-server: 17.2
python311: 3.11.10
readline: 8.2.13_1
zstd: 1.5.6
Number of packages to be installed: 17
The process will require 1 GiB more space.
231 MiB to be downloaded.
Proceed with this action? [y/N]: y
[1/17] Fetching indexinfo-0.3.1.pkg: 100% 6 KiB 5.9kB/s 00:01
[2/17] Fetching libxml2-2.11.9.pkg: 100% 873 KiB 893.6kB/s 00:01
...
To use PostgreSQL, enable it in rc.conf using
sysrc postgresql_enable=yes
To initialize the database, run
service postgresql initdb
You can then start PostgreSQL by running:
service postgresql start
For postmaster settings, see ~postgres/data/postgresql.conf
NB. FreeBSD's PostgreSQL port logs to syslog by default
See ~postgres/data/postgresql.conf for more info
NB. If you're not using a checksumming filesystem like ZFS, you might
wish to enable data checksumming. It can be enabled during
the initdb phase, by adding the "--data-checksums" flag to
the postgresql_initdb_flags rcvar. Otherwise you can enable it later by
using pg_checksums. Check the initdb(1) manpage for more info
and make sure you understand the performance implications.
We can easily verify this by looking at /usr/local/bin of our base:
root@freebsd14:~ $ ls -al /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg*
-rwxr-xr-x 1 root wheel 99224 Dec 3 02:06 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_amcheck
-rwxr-xr-x 1 root wheel 43616 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_archivecleanup
-rwxr-xr-x 1 root wheel 161128 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_basebackup
-rwxr-xr-x 1 root wheel 78336 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_checksums
-rwxr-xr-x 1 root wheel 53784 Dec 3 02:06 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_config
-rwxr-xr-x 1 root wheel 57272 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_controldata
-rwxr-xr-x 1 root wheel 101656 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_createsubscriber
-rwxr-xr-x 1 root wheel 72136 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_ctl
-rwxr-xr-x 1 root wheel 422632 Dec 3 02:06 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_dump
-rwxr-xr-x 1 root wheel 121896 Dec 3 02:06 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_dumpall
-rwxr-xr-x 1 root wheel 67904 Dec 3 02:06 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_isready
-rwxr-xr-x 1 root wheel 103840 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_receivewal
-rwxr-xr-x 1 root wheel 101768 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_recvlogical
-rwxr-xr-x 1 root wheel 68312 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_resetwal
-rwxr-xr-x 1 root wheel 207752 Dec 3 02:06 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_restore
-rwxr-xr-x 1 root wheel 153560 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_rewind
-rwxr-xr-x 1 root wheel 49024 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_test_fsync
-rwxr-xr-x 1 root wheel 39088 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_test_timing
-rwxr-xr-x 1 root wheel 178256 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_upgrade
-rwxr-xr-x 1 root wheel 110984 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pg_waldump
-rwxr-xr-x 1 root wheel 207416 Dec 3 02:06 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pgbench
root@freebsd14:~ $ ls -al /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/po*
-rwxr-xr-x 1 root wheel 4149 Oct 31 02:09 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pod2html
-rwxr-xr-x 1 root wheel 15046 Oct 31 02:09 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pod2man
-rwxr-xr-x 1 root wheel 10815 Oct 31 02:09 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pod2text
-rwxr-xr-x 1 root wheel 4113 Oct 31 02:09 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/pod2usage
-rwxr-xr-x 1 root wheel 3664 Oct 31 02:09 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/podchecker
-rwxr-xr-x 1 root wheel 9548040 Dec 3 02:18 /usr/local/jails/templates/14.1-RELEASE/usr/local/bin/postgres
In the same way we can ask pkg to operate in a chroot, we can also ask sysrc to operate in a chroot and directly enable the PostgreSQL service:
root@freebsd14:~ $ sysrc -R /usr/local/jails/templates/14.1-RELEASE/ postgresql_enable=yes
postgresql_enable: -> yes
root@freebsd14:~ $ cat /usr/local/jails/templates/14.1-RELEASE/etc/rc.conf
postgresql_enable="yes"
Now we have everything we need and can create a new ZFS snapshot:
root@freebsd14:~ $ zfs snapshot zroot/jails/templates/14.1-RELEASE@postgresql17
root@freebsd14:~ $ zfs list -t snapshot
NAME USED AVAIL REFER MOUNTPOINT
zroot/ROOT/default@2024-11-26-10:56:43-0 394M - 1.46G -
zroot/ROOT/default@2024-12-04-08:41:41-0 2.23M - 2.49G -
zroot/ROOT/default@2024-12-04-08:42:46-0 1.86M - 2.58G -
zroot/jails/templates/14.1-RELEASE@postgresql17 0B - 1.34G -
Looking at the configuration for the Jail in the last post, it looks like this:
root@freebsd14:~ $ cat /etc/jail.conf.d/thinjail1.conf
thinjail1 {
# STARTUP/LOGGING
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_console_${name}.log";
# PERMISSIONS
allow.raw_sockets;
exec.clean;
mount.devfs;
# HOSTNAME/PATH
host.hostname = "${name}";
path = "/usr/local/jails/containers/${name}";
# NETWORK
ip4.addr = 192.168.122.160;
interface = vtnet0;
}
The dynamic parts in that configuration are the IP address, the path, the hostname and the name of the Jail. Everything else remains static for additional Jails. So we could create a simple script which creates a new Jail configuration based on a few parameters (there is no error handling, no sanity checks, and everything else which makes a good script a good script, this really is just for demonstration purposes):
#!/usr/local/bin/bash
# The parameters for a new Jail
JAILNAME="$1"
IPADDR="$2"
# This is where all the Jails go to
BASEPATH="/usr/local/jails/containers/"
cat << EOF > /etc/jail.conf.d/${JAILNAME}.conf
${JAILNAME} {
# STARTUP/LOGGING
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.consolelog = "/var/log/jail_console_${name}.log";
# PERMISSIONS
allow.raw_sockets;
allow.sysvipc = 1;
exec.clean;
mount.devfs;
# HOSTNAME/PATH
host.hostname = "${JAILNAME}.it.dbi-services.com";
path = "${BASEPATH}/${JAILNAME}";
# NETWORK
ip4.addr = ${IPADDR};
interface = vtnet0;
}
EOF
zfs clone zroot/jails/templates/14.1-RELEASE@postgresql17 zroot/jails/containers/${JAILNAME}
service jail start ${JAILNAME}
jls
jexec -U postgres ${JAILNAME} /usr/local/bin/initdb --pgdata=/var/db/postgres/data17
jexec -l ${JAILNAME} service postgresql start
What this script is doing:
- Create the Jail configuration based on the name and IP address
- Clone the ZFS snapshot we’ve created above
- Start the Jail
- Initialize PostgreSQL
- Start up PostgreSQL
If you run that it looks like this:
root@freebsd14:~ $ ./new_jail.sh PG1 192.168.122.130
Starting jails: PG1.
JID IP Address Hostname Path
11 192.168.122.130 PG1.it.dbi-services.com /usr/local/jails/containers/PG1
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.
The database cluster will be initialized with locale "C.UTF-8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".
Data page checksums are disabled.
creating directory /var/db/postgres/data17 ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default "max_connections" ... 100
selecting default "shared_buffers" ... 128MB
selecting default time zone ... Europe/Vaduz
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok
initdb: warning: enabling "trust" authentication for local connections
initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb.
Success. You can now start the database server using:
/usr/local/bin/pg_ctl -D /var/db/postgres/data17 -l logfile start
2024-12-04 10:28:32.103 CET [4809] LOG: ending log output to stderr
2024-12-04 10:28:32.103 CET [4809] HINT: Future log output will go to log destination "syslog".
That’s it, PostgreSQL is up and running in the Jail:
root@freebsd14:~ $ psql -h 192.168.122.130
psql: error: connection to server at "192.168.122.130", port 5432 failed: FATAL: no pg_hba.conf entry for host "192.168.122.130", user "root", database "root", no encryption
Pretty easy to do. Now additional PostgreSQL Jails can be added easily. What is really impressive is the size of the Jail on disk:
root@freebsd14:~ $ du -sh /usr/local/jails/containers/PG1
1.4G /usr/local/jails/containers/PG1
A bit more than a GB for a complete PostgreSQL service.