Daniel Littlewood <[email protected]> writes:

> Hi help-guix,

Hi Dan,

> I am trying to create a database container with persistent data. I am
> following the example in the cookbook
> (https://guix.gnu.org/cookbook/en/html_node/A-Database-Container.html).
> Since containers are ephemeral, I think the data written to the
> database will be wiped every time I close the process.
>
> .................
>
> I'm posting here in the hopes that someone else has thought about the
> problem in more detail.

As a matter of fact, I run multiple server clusters at my job using Guix, 
including PostgreSQL servers.

Here's how I solve the problem that you've outlined:

1. Select non-conflicting UIDs and GIDs on your host and set these in 
environment variables. You can just select some high values or use code like 
this to reuse your host's postgres UID and GID (if this user exists on your 
system):

```sh
export POSTGRES_UID=$(id -u postgres 2> /dev/null)
export POSTGRES_GID=$(getent group postgres | cut -d: -f3)
```

2. Select a persistent state folder on your host that will hold the Postgresql 
database contents. For example:

```sh
MY_GUIX_STATE_DIR="/somewhere/you/can/write/to"
POSTGRES_VAR_LIB_POSTGRESQL="$MY_GUIX_STATE_DIR/postgres/var/lib/postgresql"

mkdir -p $POSTGRES_VAR_LIB_POSTGRESQL

for DIR in $POSTGRES_VAR_LIB_POSTGRESQL
do
    sudo chmod -R 700 $DIR
    sudo chown -R $POSTGRES_UID:$POSTGRES_GID $DIR
done

export POSTGRES_BASE_DIR="$POSTGRES_VAR_LIB_POSTGRESQL=/var/lib/postgresql"
```

3. Select a non-conflicting port for the PostgreSQL server to bind to:

```sh
export POSTGRES_PORT=31337
```

4. Create a pg_hba.conf file to specify the authentication rules for your 
server, and set its path in an environment variable. For example:

```sh
export POSTGRES_PG_HBA_CONF=pg_hba.conf

cat > $POSTGRES_PG_HBA_CONF <<EOF
local all all              trust
host  all all 127.0.0.1/32 trust
host  all all ::1/128      trust
EOF
```

5. Create your Guix system container script like so:

```sh
CONTAINER_SCRIPT=$(guix time-machine -C channels.scm -- system container 
--network --share=$POSTGRES_BASE_DIR postgres-container.scm)
```

6. Run the script as root:

```sh
sudo $CONTAINER_SCRIPT
```

7. In another terminal, enter the container like so:

```sh
PID=$(pgrep -n shepherd)
sudo guix container exec $PID /run/current-system/profile/bin/bash --login
```

And here is my postgres-container.scm file:

```scheme
(use-modules
 ((gnu bootloader grub)     #:select (grub-bootloader))
 ((gnu bootloader)          #:select (bootloader-configuration))
 ((gnu packages databases)  #:select (postgresql-16))
 ((gnu packages geo)        #:select (postgis))
 ((gnu packages tmux)       #:select (tmux))
 ((gnu services base)       #:select (%base-services 
virtual-terminal-service-type))
 ((gnu services databases)  #:select (postgresql-service-type 
postgresql-configuration postgresql-config-file))
 ((gnu services)            #:select (service modify-services delete))
 ((gnu system file-systems) #:select (file-system %base-file-systems))
 ((gnu system shadow)       #:select (%base-user-accounts))
 ((gnu system)              #:select (operating-system %base-packages))
 ((guix gexp)               #:select (local-file assume-valid-file-name))
 ((guix packages)           #:select (package modify-inputs package-inputs 
replace)))

(define postgis-for-postgresql-16
  (package
   (inherit postgis)
   (inputs (modify-inputs (package-inputs postgis)
                          (replace "postgresql" postgresql-16)))))

(define (extract-container-dir dir-spec)
  (car (last-pair (string-split dir-spec #\=))))

(let ((uid      (string->number (or (getenv "POSTGRES_UID") "")))
      (gid      (string->number (or (getenv "POSTGRES_GID") "")))
      (port     (string->number (or (getenv "POSTGRES_PORT") "")))
      (hba-file (or (getenv "POSTGRES_PG_HBA_CONF") "pg_hba.conf"))
      (base-dir (extract-container-dir (or (getenv "POSTGRES_BASE_DIR") 
"/var/lib/postgresql"))))
  (operating-system
   (host-name (string-append "postgres-container-on-" (gethostname)))
   (timezone "Etc/UTC")
   (locale "en_US.utf8")

   ;; No bootloader needed when running as a container
   (bootloader (bootloader-configuration
                (bootloader grub-bootloader)
                (targets '("does-not-matter"))))

   ;; No filesystems needed when running as a container
   (file-systems (cons (file-system
                        (device "does-not-matter")
                        (mount-point "/")
                        (type "does-not-matter"))
                       %base-file-systems))

   (users %base-user-accounts)

   (packages (cons* tmux
                    postgresql-16
                    %base-packages))

   (services (cons* (service postgresql-service-type
                             (postgresql-configuration
                              (postgresql postgresql-16)
                              (port port)
                              (locale "en_US.utf8")
                              (config-file (postgresql-config-file
                                            (log-destination "syslog")
                                            (hba-file (local-file 
(assume-valid-file-name hba-file)))
                                            (socket-directory 
"/var/run/postgresql")
                                            (extra-config '(("listen_addresses" 
"*")
                                                            ("log_directory" 
"/var/log/postgresql")))))
                              (data-directory (string-append base-dir "/data"))
                              (extension-packages (list 
postgis-for-postgresql-16))
                              (create-account? #t)
                              (allow-login? #f)
                              (home-directory "/var/empty")
                              (uid uid)
                              (gid gid)))
                    (modify-services %base-services
                                     (delete virtual-terminal-service-type))))))
```

Finally, here is my channels.scm file for completeness:

```scheme
(list (channel
       (name 'guix)
       (url "https://git.guix.gnu.org/guix.git";)
       (branch "master")
       (commit
        "07ba811e8969f7207a072266073b15bc8e356675")
       (introduction
        (make-channel-introduction
         "9edb3f66fd807b096b48283debdcddccfea34bad"
         (openpgp-fingerprint
          "BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA")))))
```

Alright, hopefully that will get you running with your own containerized 
PostgreSQL server. Have fun and happy hacking!

~Gary

-- 
GPG Key ID: C4FBEDBD
Use `gpg --search-keys [email protected]' to find me
Protect yourself from surveillance: https://emailselfdefense.fsf.org
=======================================================================
()  ascii ribbon campaign - against html e-mail
/\  www.asciiribbon.org   - against proprietary attachments

Why is HTML email a security nightmare? See https://useplaintext.email/

Please avoid sending me MS-Office attachments.
See http://www.gnu.org/philosophy/no-word-attachments.html

Reply via email to