home / blog index | Installing Nextcloud in Podman

Nextcloud on Podman.

Hi! In this guide we will set up NextCloud that will run on rootless Podman pod. Guide has been tested on Debian 11 Bullseye which hasn’t released yet. There might be differences especially in Podman commands if you are using older distro.

Installing Podman, Nginx and Certbot.

sudo apt update
sudo apt install podman nginx python3-certbot

Don’t use Certbot nginx integration because it clutters nginx config and won’t work with VPN setup if you want to use it.

Setup nginx

Requesting wildcard SSL cert with Certbot.

sudo certbot certonly --manual \
  --preferred-challenges=dns \
  --email admin@example.com \
  --server https://acme-v02.api.letsencrypt.org/directory \
  --agree-tos \
  --manual-public-ip-logging-ok \
  -d "*.example.com" \
  -d example.com

Certbot will ask you to create TXT record in DNS zone. Just make what it says. After creating TXT record don’t go further in Certbot immediately because changing values in DNS will take some time to appear globally. You can check if TXT record is visible by running

[nat@apricot ~]$ nslookup
> set type=txt
> _acme-challenge.example.com

Put this file to /etc/letsencrypt/options-ssl-nginx.conf

Generate strong DH parameters (it may take few minutes to complete)

# cd /etc/letsencrypt
# openssl dhparam -out dhparam.pem 4096

DH parameters will stop attackers from decrypting traffic even after they will somehow get our private SSL keys.

Keep in mind that this is very basic (but for you - most probably enough) security. If you want something more secure then take a look at nginx config used by GrapheneOS

Install nginx config

Copy contents of cloud.example.com file from bottom of this article to /etc/nginx/sites-available and change its contents with your domain etc.

Activate site

sudo ln -s /etc/nginx/sites-available/cloud.example.com /etc/nginx/sites-enabled/cloud.example.com
sudo systemctl restart nginx

Prepare user for running rootless containers

Create user.

sudo adduser nextcloud

Create files and folders

sudo mkdir -p /opt/nextcloud/{db,config,data}
sudo chown -R nextcloud:nextcloud /opt/nextcloud

Switch to new user

sudo su nextcloud
cd ~

Create rootless pod

Pods are sharing ports between every container at It’s way easier to understand than Docker hacky IPv4 implementation and doesn’t mess with your firewall configuration.

podman pod create --name nextcloud -p 9999:443

-p 9999:443 means that port 9999 on host will be forwarded to 443 in container. You should use this nftables config to prevent others from connecting directly, bypassing nginx reverse proxy/SSL.

Create containers inside pod

If you have RHEL/Fedora based distro (or anything else with SELinux enabled), then at the end of --volume arguments you must add :Z e.g.

--volume /opt/nextcloud/db:/var/lib/postgresql/data:Z \

PostgreSQL Database

podman run --detach \
  -e PUID=1001 \
  -e PGID=1001 \
  --env PGDATA=/var/lib/postgresql/data/pgdata \
  --env POSTGRES_DB=nextcloud \
  --env POSTGRES_USER=nextcloud \
  --env POSTGRES_PASSWORD=sqlpassword \
  --volume /opt/nextcloud/db:/var/lib/postgresql/data \
  --pod nextcloud \
  --restart on-failure \
  --name nextcloud-db \


podman run --detach \
  --pod nextcloud \
  --name nextcloud-redis \
  --restart on-failure \


Change TZ=Europe/Warsaw to your timezone.

podman run --detach \
  --pod nextcloud \
  --name nextcloud-app \
  --env TZ=Europe/Warsaw \
  --volume /opt/nextcloud/config:/config \
  --volume /opt/nextcloud/data:/data \
  --restart unless-stopped \

Setup NextCloud

Connect to https://cloud.example.com and fill setup with your database credentials. Don’t forget to change database type from sqlite to PostgreSQL. As database host input localhost:5432 (You can see now how easy networking inside Podman is :))

DON’T check install recommended apps. It will install Collabora etc. that won’t work inside NextCloud containers. It has to be installed on a separate container, and it won’t be described here as I never used it. If you installed recommended apps then you can set up nextcloud-app Container again or just remove Collabora apps from WebUI.

Fixing NextCloud config

First run issue

NextCloud is now setup properly, but we can’t use it because we got redirected to https://localhost:9999. To fix that, we have two choices. Files inside /opt/nextcloud are now owned by users inside containers, and we can’t access them.

How to deal with permissions

The file that we have to edit is /opt/nextcloud/config/www/nextcloud/config/config.php

We can switch to user with sudo permissions and run sudo nano /opt/nextcloud/config/www/nextcloud/config/config.php

Or the correct way - by using podman unshare. podman unshare nano /opt/nextcloud/config/www/nextcloud/config/config.php

This will launch nano with permissions of container allowing us to modify this file.


Change this line

  'overwrite.cli.url' => 'https://localhost:9999',


  'overwrite.cli.url' => 'https://cloud.example.com',

and add this line to config

  'overwritehost' => 'cloud.example.com',

Adding region code

Inside config.php add this line

  'default_phone_region' => 'US',

Change US to code of your country from here

Setup Redis

Paste this inside your config.php

  'memcache.distributed' => '\OC\Memcache\Redis',
  'memcache.locking' => '\OC\Memcache\Redis',
  'redis' => [
      'host' => 'localhost',
      'port' => 6379,

Autostart with systemd

Enable linger with

sudo loginctl enable-linger nextcloud

Switch back to nextcloud user with

sudo su nextcloud

Create systemd directory by running

mkdir -p ~/.config/systemd/user

Switch directory with

cd ~/.config/systemd/user

Generate systemd services with

podman generate systemd -f --name nextcloud

Activate services by running

systemctl --user daemon-reload
systemctl --user enable pod-nextcloud.service

NextCloud will now autostart after booting ✨

Updating NextCloud fully

To update NextCloud - stop entire pod with

podman pod stop nextcloud

and remove it with

podman pod rm nextcloud

After that create pod and all containers again and follow update procedure from here

nginx vhost config

server {
    server_name cloud.example.com;

    index index.html;

    location / {
        proxy_pass https://localhost:9999/;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
        client_max_body_size 0;

        access_log /var/log/nginx/nextcloud.access.log;
        error_log /var/log/nginx/nextcloud.error.log;

    location ^~ /.well-known {
        # The following 6 rules are borrowed from `.htaccess`

        location = /.well-known/carddav     { return 301 /remote.php/dav/; }
        location = /.well-known/caldav      { return 301 /remote.php/dav/; }
        # Anything else is dynamically handled by Nextcloud
        location ^~ /.well-known            { return 301 /index.php$uri; }

        try_files $uri $uri/ =404;

    listen 443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/dhparam.pem;