Skip to content

Instantly share code, notes, and snippets.

@e-minguez
Last active November 12, 2023 09:31
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save e-minguez/1cd7113dfdf8956f8cdd603069e033f8 to your computer and use it in GitHub Desktop.
Save e-minguez/1cd7113dfdf8956f8cdd603069e033f8 to your computer and use it in GitHub Desktop.
podman pod nextcloud
  • Some variables to avoid writting too much and create some folders:
export PODNAME="nextcloud"
mkdir -p ~/containers/nextcloud/{db,nginx,html}
  • Copy the nginx.conf file:
cp ~/nginx.conf ~/containers/nextcloud/nginx/nginx.conf
  • Create an empty pod exposing port 8080/tcp only
podman pod create --hostname ${PODNAME} --name ${PODNAME} -p 8080:80

Start adding containers to the pod.

  • MariaDB:
podman run \
  -d --restart=always --pod=${PODNAME} \
  -e MYSQL_ROOT_PASSWORD="myrootpass" \
  -e MYSQL_DATABASE="nextcloud" \
  -e MYSQL_USER="nextcloud" \
  -e MYSQL_PASSWORD="mynextcloudpass" \
  -v ${HOME}/containers/nextcloud/db:/var/lib/mysql:z \
  --name=${PODNAME}-db docker.io/library/mariadb:latest \
  --transaction-isolation=READ-COMMITTED --binlog-format=ROW

It will listen into the 3306/tcp port ONLY within the pod.

  • Redis:
podman run \
  -d --restart=always --pod=${PODNAME} \
  --name=${PODNAME}-redis docker.io/library/redis:alpine \
  redis-server --requirepass yourpassword

It will listen into the 6379/tcp port ONLY within the pod.

  • Nextcloud App:
podman run \
  -d --restart=always --pod=${PODNAME} \
  -e REDIS_HOST="localhost" \
  -e REDIS_HOST_PASSWORD="yourpassword" \
  -e MYSQL_HOST="localhost" \
  -e MYSQL_USER="nextcloud" \
  -e MYSQL_PASSWORD="mynextcloudpass" \
  -e MYSQL_DATABASE="nextcloud" \
  -v ${HOME}/containers/nextcloud/html:/var/www/html:Z \
  --name=${PODNAME}-app docker.io/library/nextcloud:fpm-alpine

It will listen into the 9000/tcp port ONLY within the pod.

  • Nextcloud Cron:
podman run \
  -d --restart=always --pod=${PODNAME} \
  -v ${HOME}/containers/nextcloud/html:/var/www/html:Z \
  --entrypoint=/cron.sh \
  --name=${PODNAME}-cron docker.io/library/nextcloud:fpm-alpine

It will listen into the 9000/tcp port ONLY within the pod.

  • Nginx:
podman run \
  -d --restart=always --pod=${PODNAME} \
  -v ${HOME}/containers/nextcloud/html:/var/www/html:ro,Z \
  -v ${HOME}/containers/nextcloud/nginx/nginx.conf:/etc/nginx/nginx.conf:ro,z \
  --name=${PODNAME}-nginx docker.io/library/nginx:alpine

It will listen into the 80/tcp port... and as the pod expose that port as 8080/tcp in the host, you can reach the app!

Nextcloud installation

podman exec -it -u www-data nextcloud-app /bin/sh
php occ maintenance:install \
  --database "mysql" \
  --database-host "127.0.0.1" \
  --database-name "nextcloud" \
  --database-user "nextcloud" \
  --database-pass "mynextcloudpass" \
  --admin-pass "password" \
  --data-dir "/var/www/html"
php occ config:system:set \
  trusted_domains 1 --value=192.168.1.98
php occ config:system:set \
  trusted_domains 2 --value=nextcloud.example.com
php occ config:system:set \
  check_data_directory_permissions --value="false" --type=boolean
php occ config:system:set \
  overwrite.cli.url --value "https://nextcloud.example.com"
php occ config:system:set \
  overwriteprotocol --value "https"
exit

The reason behind the directory permissions is here. Basically, NextCloud resets the data directory permissions to 770, but nginx requires to access that folder, otherwise it complains about file not found. I tried to use --group-add flags to force group allocation of the user running both nginx and nextcloud but they run as root and then they change to a different user (www-data and nginx) so the group is not inherited...

sudo chmod 775 ~/containers/nextcloud/html
podman pod restart nextcloud

Services (TO DO)

The official documentation says to generate the unit files using podman generate systemd but I don't like the way it creates it (see this blog post for more information).

Instead, I prefer to create this kind of systemd unit files

[Unit]
Description=Podman container-mosquitto.service

[Service]
Restart=on-failure
ExecStartPre=/usr/bin/rm -f /%t/%n-pid /%t/%n-cid
ExecStart=/usr/bin/podman run --conmon-pidfile /%t/%n-pid --cidfile /%t/%n-cid \
		-d -p 1883:1883 -p 9001:9001 \
		-v /home/edu/containers/mosquitto/config/:/mosquitto/config/:Z\
		 --name mosquitto \
		eclipse-mosquitto
ExecStop=/usr/bin/podman stop -t 10 eclipse-mosquitto
#ExecStop=/usr/bin/podman stop --ignore --cidfile %t/%n-cid -t 10
#ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/%n-cid
ExecStopPost=/usr/bin/sh -c "/usr/bin/podman rm -f `cat /%t/%n-cid`"
KillMode=none
Type=forking
PIDFile=/%t/%n-pid

[Install]
WantedBy=default.target
systemctl --user daemon-reload
systemctl --user enable pod-nextcloud.service
sudo loginctl enable-linger $(whoami)

The reason behind modifying the unit files is here

Firewall

sudo firewall-cmd --add-port=8080/tcp
sudo firewall-cmd --add-port=8080/tcp --permanent
# Basically https://github.com/nextcloud/docker/blob/master/.examples/docker-compose/with-nginx-proxy/mariadb-cron-redis/fpm/web/nginx.conf
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 172.16.0.0/12;
set_real_ip_from 192.168.0.0/16;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
#gzip on;
upstream php-handler {
server 127.0.0.1:9000;
}
server {
listen 80;
# Add headers to serve security related headers
# Before enabling Strict-Transport-Security headers please read into this
# topic first.
# add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
#
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
# Remove X-Powered-By, which is an information leak
fastcgi_hide_header X-Powered-By;
# Path to the root of your installation
root /var/www/html;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
# The following 2 rules are only needed for the user_webfinger app.
# Uncomment it if you're planning to use this app.
#rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
#rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json
# last;
# Removed server_port as https://github.com/nextcloud/docker/issues/577
location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}
# set max upload size
client_max_body_size 10G;
fastcgi_buffers 64 4K;
# Enable gzip but do not remove ETag headers
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
# Uncomment if your server is build with the ngx_pagespeed module
# This module is currently not supported.
#pagespeed off;
location / {
rewrite ^ /index.php;
}
location ~ ^\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
deny all;
}
location ~ ^\/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
# fastcgi_param HTTPS on;
# Avoid sending the security headers twice
fastcgi_param modHeadersAvailable true;
# Enable pretty urls
fastcgi_param front_controller_active true;
fastcgi_pass php-handler;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
}
location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}
# Adding the cache control header for js, css and map files
# Make sure it is BELOW the PHP block
location ~ \.(?:css|js|woff2?|svg|gif|map)$ {
try_files $uri /index.php$request_uri;
add_header Cache-Control "public, max-age=15778463";
# Add headers to serve security related headers (It is intended to
# have those duplicated to the ones above)
# Before enabling Strict-Transport-Security headers please read into
# this topic first.
#add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;
#
# WARNING: Only add the preload option once you read about
# the consequences in https://hstspreload.org/. This option
# will add the domain to a hardcoded list that is shipped
# in all major browsers and getting removed from this list
# could take several months.
add_header Referrer-Policy "no-referrer" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Download-Options "noopen" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Permitted-Cross-Domain-Policies "none" always;
add_header X-Robots-Tag "none" always;
add_header X-XSS-Protection "1; mode=block" always;
# Optional: Don't log access to assets
access_log off;
}
location ~ \.(?:png|html|ttf|ico|jpg|jpeg|bcmap)$ {
try_files $uri /index.php$request_uri;
# Optional: Don't log access to other assets
access_log off;
}
}
}
@eriksjolund
Copy link

Basically, NextCloud resets the data directory permissions to 770, but nginx requires to access that folder, otherwise it complains about file not found.

I think this problem can be circumvented by using different UID/GID-mappings for each container.

This gist runs containers in a pod with the command-line option --pod=${PODNAME} .
By running containers without --pod=${PODNAME}, it's possible to have different UID/GID-mappings for the containers.
I tried it out. Two empty directories were bind-mounted as volumes. After running Nextcloud/MariaDB/nginx/redis, I checked the ownership of all created files and directories. They were all owned by the regular user on the host. For more info see https://github.com/eriksjolund/nextcloud-podman (The example is not based on this gist)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment