OMV RPi5 Server Build Guide
Disclaimer: everything expressed in this post is my own opinion. Logos are trademarks of their respective owners.
This guide outlines how to setup a Raspberry Pi 5 with the RADXA Penta SATA HAT to run OMV (Open Media Vault) with a ZFS filesystem, and host NextCloud and Immich using Docker and Tailscale. Please read the motivation for this build guide here: https://www.ianwebster.ca/post/omv-rpi5-server!
You'll need the following hardware components for this build:
Optional components:
This guide considers the following hardware/software configuration:
Here is a diagram of the configuration at a high level:
Here's my comprehensive guide to setting up the RPi5, hardware and software.
First, let's build the Pi by installing the Cooler and RADX Penta Sata Hat.
Raspbian is the OS that our Pi will be running. OMV will be installed on top of Raspbian.
username that is not admin, according to the OMV7 guide.Next up, we'll install OMV. All the subsections below are based on this OMV7 guide https://wiki.omv-extras.org/doku.php?id=omv7:raspberry_pi_install which you should refer to for the most updated guidance. It is possible a newer version of OMV is released by the time you read this!
user1 with the username you setup in the "Apply Custom OS Settings" earlier.ssh user1@raspberrypi.local.ip add to get the local ip address of your pi, for example, 10.0.1.89.Execute the following commands to install OMV:
# 1. Update
sudo apt-get update
# 2. Upgrade
sudo apt-get upgrade -y
# 3. Get OMV preinstall script
wget -O -Â [https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/preinstall](https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/preinstall "https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/preinstall")Â | sudo bash
# 4. When all commands finish, reboot:
sudo reboot
# 5. Your Pi IP address may have changed again since it is assigned a static ip. It should still be accessible using `raspberrypi.local`.
# 6. SSH into your Pi again as before
ssh user1@raspberrypi.local
# 7. Get OMV full install script
wget -O -Â [https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/install](https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/install "https://github.com/OpenMediaVault-Plugin-Developers/installScript/raw/master/install")Â | sudo bash
# 8. Wait up to 30 minutes for OMV7 to install.
# 9. Once the pi reboots, you can ssh into it again.
# 10. Go to your browser and type in the local ip address of the Pi from earlier, for example `10.0.1.89`. You should be redirected to the OMV login page. The web UI user is `admin` and the default password is `openmediavault`.
# 11. Make sure to update the default password!
Enabling PCIe Gen3:
# 1. SSH into your Pi
ssh user1@rasberrypi.local
# 2. Enable PCIe: Edit `/boot/firmware/config.txt` and add `dtparam=pciex1` to the end of the file, save and reboot. Use `vi` instead of `nano` if you'd like.
nano /boot/firmware/config.txt
# 2.1 Append `dtparam=pciex1` to the end of the file, save and reboot.
# 3. Check disk
$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 1.9T 0 disk
sdb 8:16 0 1.9T 0 disk
...
# 4. Speed test
$ sudo dd if=/dev/zero of=/dev/sda bs=32M status=progress count=100 oflag=direct
3154116608 bytes (3.2 GB, 2.9 GiB) copied, 8 s, 391 MB/s
100+0 records in
100+0 records out
3355443200 bytes (3.4 GB, 3.1 GiB) copied, 8.61021 s, 390 MB/s
# 5. Force PCIe Gen 3. Use `vi` instead of `nano` if you'd like.
nano /boot/firmware/config.txt
# 5.1 Append `dtparam=pciex1_gen=3` to the end of the file
# 6. Reboot
sudo reboot now
# 7. Check link status of the SATA Hat
$ sudo lspci -vvv -s 0000:01:00.0 | grep LnkSta
LnkSta: Speed 8GT/s, Width x1 (downgraded)
LnkSta2: Current De-emphasis Level: -6dB, EqualizationComplete+ EqualizationPhase1+
# 8. Speed test again
$ sudo dd if=/dev/zero of=/dev/sda bs=32M status=progress count=100 oflag=direct
2986344448 bytes (3.0 GB, 2.8 GiB) copied, 6 s, 498 MB/s
100+0 records in
100+0 records out
3355443200 bytes (3.4 GB, 3.1 GiB) copied, 6.77122 s, 496 MB/s
The link speed should now be 8GT/s. Note the increase from 390MB/s to 496MB/s.
You can install ZFS to protect your data from expected drive degradation over time. ZFS relies on using RAM as a filesystem cache, so I would recommend having at least an 8GB RPi5 to enable ZFS. I will make the assumption you have a 2x 2TB SSD setup attached to your SATA Hat for this subsection, and I will assume a Mirrored ZFS Pool with 2x 2TB SSDs. In this configuration, you will have 2TB of usable space, and if 1 drive fails, you may replace it to rebuild your pool.
Steps:
10.0.1.89 for example.System > Plugins and search for openmediavault-zfs. As of writing this, the latest version is openmediavault-zfs 7.1.4.Storage > zfsStorage > zfs > Pools > Add > Add PoolName: e.g. `ssd-pool`
Pool type: `Mirror`
Devices: add your two SSDs here.
Mountpoint: leave as default.
Device Alias: leave as `By ID`.
Click Save. Your new pool ssd-pool should now be created! The status should be OK.
Enabling TRIM on your ZFS pool generally extends the lifespan of your SSDs.
You can check the status on TRIM and run it manually with the following commands:
# 1. Validate if trim is on
user1@raspberrypi:~ $ sudo zpool get autotrim <your_pool_name>
NAME PROPERTY VALUE SOURCE
<your_pool_name> autotrim off default
# 2. Run trim
user1@raspberrypi:~ $ sudo zpool trim <your_pool_name>
# 3. Validate status is trimming
user1@raspberrypi:~ $ zpool status
...
NAME STATE READ WRITE CKSUM
ssd-pool
mirror-0
ata-TEAM_SSD_A ONLINE 0 0 0 (trimming)
...
To set up a recurring cron job to run TRIM on your pool:
10.0.1.89 for example.System > Scheduled Tasks and add a task.Time of execution to weekly. Ideally set this to a time where your server is idle.Command to sudo zpool trim <your_pool_name>.TRIM will now run every week to maintain the health of your SSDs.
If you want to access files from your server via Finder on your Mac for example, you'll need to create a SMB share. In this section, you will create a share folder, an SMB share, and as result, you will be able to access a share folder on the ssd-pool from your Mac in Finder. You can skip this section if you don't want drag-n-drop read/write compatibility with your server, but note that some these steps are required in section 2.7.
10.0.1.89 for example.Users > Users > Create and fill out the fields below:Name: e.g. `pooluser`
Password: fill out a password. Don't forget it.
Shell: leave as is.
Groups: `users`
Click Save.
ssd-pool you just created in section 2.5.Name: e.g. `ssd-pool-share-folder`
File system: choose your ZFS pool e.g. `ssd-pool`
Relative path: e.g. `ssd-pool-share`
Tags: whatever you want.
Click Save.
ssd-pool-share-folder and click Permissions at the top nav bar. Allow your new pooluser to Read/Write.sdd-pool-share-folder again. Click the Access Control List this time. Scroll down to the User/Group permissions and give your pooluser Read/Write permissions too.pooluser and Shared folder ssd-pool-share-folder are created, let's create a SMB Share.Services > SMB/CIFS > Shares > Create and fill out the fields below:Enabled: checked
Shared folder: e.g. `ssd-pool-share-folder`
Public: No
Browseable: checked
Time Machine support: checked if you want this
Inherit permissions: checked
Hosts allow: e.g. `ip of your client accessing the server`. If your computer has a static IP assigned, you can set that here. If you are only accessing your server from a single computer, it makes sense to use the allow list to secure access to your SMB Share.
Click save.
smb:// prefix, e.g. smb://10.0.1.89. You should see your share folder ssd-pool-share-folder .pooluser and password you created earlier.Let's prepare for installing Docker by setting up our share folders. We'll need a backup folder, a compose folder, and an appdata folder.
Create 3 more Share Folders, using the same process as in section 2.6.2:
# 1. Docker Backup Share Folder
Name: docker-backup
File system: ssd-pool
Relative path: data/docker/backup
Tags: backup, docker
# 2. Docker Compose Share Folder
Name: docker-compose
File system: ssd-pool
Relative path: data/docker/compose
Tags: compose, docker
# 3. Docker Data Share Folder
Name: docker-data
File system: ssd-pool
Relative path: data/docker/appdata
Tags: appdata, docker
If you would like, you can setup the Permissions and the Access Control List in the same way before if you want to have access to your Docker container data via your Finder on your Mac, e.g. NextCloud and Immich. My recommendation is not to do so.
Now let's install Docker to host your NextCloud and Immich instances. Each instance will have its own container, and its own Tailscale container.
10.0.1.89 for example.System > Plugins and search for openmediavault-compose. As of writing this, the latest version is openmediavault-compose 7.6.12.Services > Compose to verify your Docker plugin is successfully installed.Tailscale is a pre-requisite to accessing your server's NextCloud and Immich instances outside your home network (although we'll also use Tailscale for home network access in this guide). As an alternative, you could setup port forwarding in your router, and use a reverse proxy like Caddy to forward external requests to either the NextCloud instance or the Immich instance. Tailscale offers a solution that avoids the hassle of port forwarding on your router.
DNS > Tailnet name. Choose a name that you are content with. This is the suffix of the URL you will be using to access both your NextCloud and Immich instances from the open internet, outside your network. For example, if your tailnet name is my-tailnet.ts.net, then your NextCloud and Immich instances would be accessible from nextcloud.my-tailnet.ts.net and immich.my-tailnet.ts.net respectively.ABC123 and client secret e.g. tskey-client-XYZ456.DNS > MagicDNS > Enable MagicDNS (https://tailscale.com/kb/1153/enabling-https) and DNS > HTTPS Certificates > Enable HTTPS. This allows you to access your NextCloud and Tailscale instances inside and outside your network using HTTPS with TLS encryption, e.g. https://nextcloud.my-tailnet.ts.net.We'll revisit Tailscale after installing NextCloud.
To install NextCloud, you'll need to create Docker compose file. You can do this by SSHing into the Pi, creating the Compose file, and using the CLI to build the compose file, but the more convenient approach for OMV is to copy and paste it into the UI.
To get started, let's build the Docker Compose file. I have adapted the compose file from this GitHub discussion from NextCloud: https://github.com/nextcloud/all-in-one/discussions/5439. I made a few improvements to make this file more OMV-friendly, such as inline-ing the Caddyfile and Tailscale Serve.json file. Ideally, you would keep the Caddyfile and Serve.json files separate, but we are limited by the OMV UI 🙂
10.0.1.89 for example.Services > Compose > Add and fill out the fields below:nextcloudnextcloud.yml from here https://gist.github.com/iWebster28/37fb9fb7a6ae9708ad2ad461ce3d0a92. Read it thoroughly, be sure to replace the environment variables, and update any parameters you see updated from the community discussion on https://github.com/nextcloud/all-in-one/discussions/5439. You will need to set TS_AUTH_KEY in nextcloud.env to the Tailscale OAuth client key from section 2.9: e.g. tskey-client-XYZ456. Also make note of the tag, tag:nextcloud in nextcloud.yml - you will need that for later in updating the Tailscale ACL.nextcloud.env from the Gist. Read it thoroughly and update it accordingly.Services > Compose > Files, select your nextcloud file, click Check to verify if there are any errors in the file. Once this passes, click up to create and start your NextCloud containers!Status will change to Up. You can now navigate to the NextCloud install.10.0.1.89:8080.nextcloud.env, your NC_DOMAIN environment variable is nextcloud.my-tailnet.ts.net, this will be your NextCloud AIO instance IP!Install NextCloud Hub 10 for the latest features. You can always use Hub 9 if you would like.You can optionally add a backup for your NextCloud config. Under Create a backup location, add ssd-pool/docker-backup that you created earlier in section 2.7 under OMV > Docker > Compose > Settings. Please review the nextcloud.yml to understand the implication of using CHANGE_TO_COMPOSE_DATA_PATH. Also navigate to and read this link http://10.0.1.89/#/services/compose/settings (replacing your static ip accordingly). Importantly, by setting our NEXTCLOUD_DATADIR: CHANGE_TO_COMPOSE_DATA_PATH/nextcloud_data in nextcloud.yml https://gist.github.com/iWebster28/37fb9fb7a6ae9708ad2ad461ce3d0a92#file-nextcloud-yml-L18, you are storing all user-uploaded data outside of our NextCloud container, e.g. docker-data/nextcloud_data - this means that user-uploaded data won't be backed up by default.
sudo cd /ssdpool/data/docker/appdata/nextcloud_data. From Finder, docker-data > nextcloud_data.CHANGE_TO_COMPOSE_DATA_PATH:CHANGE_TO_COMPOSE_DATA_PATH is that if you ever need to prune (delete) your NextCloud Docker containers, then the user data will conveniently be retained in the nextcloud_data folder. You can always delete the user data manually if you want to do a completely fresh install.Note: It is possible to add the NEXTCLOUD_DATADIR from nextcloud.yml https://gist.github.com/iWebster28/37fb9fb7a6ae9708ad2ad461ce3d0a92#file-nextcloud-yml-L18 as a backup location, if you are curious:
Back up additional directories and docker volumes of your host: Below you can enter directories and docker volumes of your host that will be backed up into the same borg backup archive. Make sure to press the submit button after changing anything.
Now that you have your NextCloud containers running, the next step is to revisit Tailscale to ensure your can access your NextCloud instance from inside and outside your network. Since we have NextCloud running behind a reverse proxy (Caddy) and Tailscale, you will need to access it from nextcloud.my-tailnet.ts.net, even on your own network.
tag:nextcloud in nextcloud.yml - you need this to update your Tailscale ACL. // Define the tags which can be applied to devices and by which users.
"tagOwners": {
"tag:nextcloud": ["your_email@mail_provider.com"],
},
Next, SSH into your server to get an HTTPS cert for your domain nextcloud.my-tailnet.ts.net:
# 1. SSH into your server
ssh user1@raspberrypi.local
# 2. Find your tailscale container, e.g. `nextcloud-nextcloud-aio-tailscale`
docker ps | grep tailscale
# 3. Open a shell inside the Tailscale container
docker exec -it nextcloud-nextcloud-aio-tailscale sh
# 4. Get HTTPS cert to verify funnel works
/ # tailscale cert nextcloud.my-tailnet.ts.net
# 5. Exit the container
/ # exit
(Development purposes only; do not do this otherwise) Copy your key and certificate to a temporary directory and delete them once your setup is working. This will save you the hassle of needing to regenerate keys and hitting the ACME request limit for generating HTTPS certs. You can see what HTTPS certs you have generated here: https://crt.sh/?q=nextcloud.my-tailnet.ts.net (replace your domain name).
# 1. Copy cert to your temporary directory and delete them later
docker container cp nextcloud-nextcloud-aio-tailscale:nextcloud.my-tailnet.ts.net.crt /home/user1/tailscale-keys-to-delete
# 2. Copy key to your temporary directory and delete them later
docker container cp nextcloud-nextcloud-aio-tailscale:nextcloud.my-tailnet.ts.net.key /home/user1/tailscale-keys-to-delete
If you want to copy these keys back to your tailscale container:
docker container cp /home/user1/tailscale-keys-to-delete/nextcloud.my-tailnet.ts.net.crt nextcloud-nextcloud-aio-tailscale:/
docker container cp /home/user1/tailscale-keys-to-delete/nextcloud.my-tailnet.ts.net.key nextcloud-nextcloud-aio-tailscale:/
Then navigate to https://login.tailscale.com/admin/machines and remove the old machine, and any new machine e.g. nextcloud-1.
Restart the Tailscale container:
docker restart nextcloud-nextcloud-aio-tailscale
nextcloud.my-tailnet.ts.net from your browser on your computer or phone. Login using your admin account. You can create new users from there!
Now that you have an HTTPS certificate, you should be able to access your NextCloud instance from your home network when you are logged into the Tailscale client on your computer or phone.To open your server to the open internet, you can use Tailscale's beta feature called Funnel.
// Funnel
"nodeAttrs": [
{"target": ["tag:nextcloud"], "attr": ["funnel"]},
],
Setting up Immich is similar to sections 2.9 through 2.12 for NextCloud!
ABC123 and client secret e.g. tskey-client-XYZ789.10.0.1.89 for example.Services > Compose > Add.immichimmich.yml from here https://gist.github.com/iWebster28/ae0ba47184204d1d094bb5f222016736. Read it thoroughly, be sure to replace the environment variables, and update any parameters you see updated latest docker file https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml and discussion on the guide https://immich.app/docs/install/docker-compose. You will need to set TS_AUTH_KEY in immich.env to the new Tailscale OAuth client key that you just made: e.g. tskey-client-XYZ789. Also make note of the tag, tag:immich in immich.yml - you will need that for later in updating the Tailscale ACL.immich.env from the Gist. Read it thoroughly and update it accordingly. Services > Compose > Files, select your immich file, click Check to verify if there are any errors in the file. Once this passes, click up to create and start your NextCloud containers!Status will change to Up.tag:immich in immich.yml - you need this to update your Tailscale ACL. // Define the tags which can be applied to devices and by which users.
"tagOwners": {
"tag:nextcloud": ["your_email@mail_provider.com"],
"tag:immich": ["your_email@mail_provider.com"],
},
SSH into your server to get an HTTPS cert for your domain immich.my-tailnet.ts.net:
# 1. SSH into your server
ssh user1@raspberrypi.local
# 2. Find your tailscale container, e.g. `immich-immich-tailscale`
docker ps | grep immich-tailscale
# 3. Open a shell
docker exec -it immich-immich-tailscale sh
# 4. Get HTTPS cert to verify funnel works
/ # tailscale cert immich.my-tailnet.ts.net
Wrote public cert to immich.my-tailnet.ts.net.crt
Wrote private key to immich.my-tailnet.ts.net.key
// Funnel
"nodeAttrs": [
{"target": ["tag:nextcloud", "tag:immich"], "attr": ["funnel"]},
],
immich.env, your IMMICH_DOMAIN environment variable is immich.my-tailnet.ts.net, this will be your Immich instance IP!You have now completed building a friends-and-family shareable, internet-accessible NextCloud and Immich instance on a Raspberry Pi 5 running OMV on Raspbian with a RADXA Penta SATA HAT using SDDs in a ZFS Mirror, drawing under 15W at load, and occupying the space of a small reusable water bottle! Great job!
Clear your browser cache, restart your browser, try incognito mode, or try another browser.
Likely, Docker is the cause. The following command clears many GB of space. Another possible solution is moving the docker build cache to the SSDs instead of the SSD card and symlinking it back on the OS drive. Alternatively, get a dedicated boot drive.
# 1. Check reclaimable space
sudo docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 39 13 16.34GB 11.49GB (70%)
Containers 14 13 951kB 0B (0%)
Local Volumes 36 15 1.984GB 7.797MB (0%)
Build Cache 23 0 1.274GB 1.274GB
# 2. Prune
sudo docker system prune -a
# 3. See reclaimed space! About 11GB.
sudo docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 12 12 6.018GB 15.89MB (0%)
Containers 13 13 951kB 0B (0%)
Local Volumes 36 15 1.984GB 7.797MB (0%)
Build Cache 0 0 0B 0B
No. Here's the exact thread that answers this: https://github.com/nextcloud/all-in-one/discussions/5439#discussioncomment-11894807.
Basically, Funnel is a beta feature that only supports forwarding ports 443 and 80. NextCloud Talk uses 3478 and 3479, which cannot be forwarded with Funnel. You can however use NextCloud talk if both clients (and server of course) are connected to the Tailnet.
Talk uses TCP and UDP, and currently, funnel forces TCP.
I have tried forcing TCP to see if Talk would work over funnel, but to no avail. This is the configuration I used in Tailscale if you're curious:
tailscale funnel 443 on
tailscale funnel 3478 on
tailscale funnel 3479 on
/ # tailscale serve status
# Funnel on:
# - https://nextcloud.my-tailnet.ts.net
# - tcp://nextcloud.my-tailnet.ts.net:3478
# - tcp://nextcloud.my-tailnet.ts.net:3479
|-- tcp://nextcloud.my-tailnet.ts.net:3478 (TLS over TCP, Funnel on)
|-- tcp://<ipv4_address>:3478
|-- tcp://[<ipv6_address>]:3478
|--> tcp://127.0.0.1:3478
|-- tcp://nextcloud.my-tailnet.ts.net:3479 (TLS over TCP, Funnel on)
|-- tcp://<ipv4_address>:3479
|-- tcp://[<ipv6_address>]:3479
|--> tcp://127.0.0.1:3479
https://nextcloud.my-tailnet.ts.net (Funnel on)
|-- / proxy http://127.0.0.1:80 # Alt for using Apache over port 11000
Regardless, the config from the main guide allows you to use Talk as long as both clients are on logged onto their Tailscale clients, which is more secure.
If your ZFS install fails when updating OMV packages, then you may see this red popover error when running docker: 500 Internal Server Error - No file system backend exists for 'zfs'. Occasionally, this happen when you update packages too.
SSH into your Pi and try loading your pool name:
ssh user1@raspberrypi.local
sudo zpool import -f <your_pool_name>
If the zpool command fails, then you may need to install the zfs packages from debian (since at the time of writing this, raspbian may not fully support the dependent zfs packages)
echo "deb http://deb.debian.org/debian bookworm-backports main contrib non-free non-free-firmware" | sudo tee /etc/apt/sources.list.d/backports.list
sudo apt update
sudo apt install -t bookworm-backports zfsutils-linux zfs-dkms
sudo dkms autoinstall
sudo modprobe zfs
And run again
sudo zpool import -f <your_pool_name>
There should be no errors. You should see your pool again at http://10.0.1.89/#/storage/zfs/pools. You may need to restart your Pi for NextCloud/Immich to pick up the ZFS pool again.
If this hangs:
/ # tailscale cert nextcloud.my-tailnet.ts.net
Check Tailscale container logs:
# 1. Check logs
docker logs nextcloud-nextcloud-aio-tailscale
...
2025/03/28 20:54:59 cert("nextcloud.my-tailnet.ts.net"): already had ACME account. 2025/03/28 20:55:59 cert("nextcloud.my-tailnet.ts.net"): getCertPEM: 429 urn:ietf:params:acme:error:rateLimited: too many certificates (5) already issued for this exact set of domains in the last 168h0m0s, retry after 2025-03-29 22:56:38 UTC: see https://letsencrypt.org/docs/rate-limits/#new-certificates-per-exact-set-of-hostnames
...
You can only request 1 cert every 34 hours. Copy the keys to an external directory as in my guide to prevent this from happening during dev!
When running this inside your Tailscale container:
/ # tailscale cert nextcloud.my-tailnet.ts.net
If you get this error:
500 Internal Server Error: your Tailscale account does not support getting TLS certs
Then make sure you have HTTPS enabled in your Tailscale account: https://login.tailscale.com/admin/dns.
# 1. Open a shell within the Tailscale container itself for debugging:
docker exec -it nextcloud-nextcloud-aio-tailscale sh
# 2. Sanity: Check the status of connected clients
/ # tailscale status
101.79.153.11 nextcloud nextcloud.my-tailnet.ts.net linux ...
101.65.97.20 your_phone your_email@mail_provider.com ...
...
# 3. Sanity: Check your ip
/ # tailscale ip -4
101.79.153.11
# 4. Request a TLS cert
/ # tailscale cert nextcloud.my-tailnet.ts.net
500 Internal Server Error: your Tailscale account does not support getting TLS certs
# 5. Fix: Enable HTTPS in your tailscale account
# 6. Re-run the cert command
/ # tailscale cert nextcloud.my-tailnet.ts.net
Wrote public cert to nextcloud.my-tailnet.ts.net.crt
Wrote private key to nextcloud.my-tailnet.ts.net.key
If you are having issues with Funnel, you can test from CLI:
# 1. Open a shell in the Tailscale container
docker exec -it nextcloud-nextcloud-aio-tailscale sh
# 2. Try to open a funnel on port 443
/ # tailscale funnel 443
# 3. You will get a link to allow Funnel access here https://login.tailscale.com/f/funnel?node=xyzABC.
# 4. Check the status of funnel
/ # tailscale serve status
# Funnel on:
# - https://nextcloud.my-tailnet.ts.net
https://nextcloud.my-tailnet.ts.net (Funnel on)
|-- / proxy http://nextcloud-aio-apache:11000
Note: tinkering with Funnel/Tags/ACL may end up creating multiple machines in your Tailscale account. You can always delete them and Tailscale will recreate them https://login.tailscale.com/admin/machines.
Sometimes, you may end up with multiple duplicates of your service/machine e.g. nextcloud, nextcloud-1, and so on. Be sure to stop your containers in OMV UI or from the CLI over SSH. You can delete your containers from the machines page https://login.tailscale.com/admin/machines and then troubleshoot the issue. Remember that Funnel uses the ACL based on the emitted tag to route traffic, so if your machine has a name of nextcloud-1 or immich-1, then it will break, and will likely create a domain name clash with NextCloud's configuration.
If you want to start over, you can optionally delete your NextCloud DATADIR. Do not do this if you do not want to lose your account and user data. This is not a requirement to start fresh if you just want to prune your containers and verify your config works.
ssh user1@raspberrypi.local
# This is a recursive deletion command; use at your own risk:
sudo rm -rf /ssdpool/data/docker/appdata/nextcloud_data
You can also run some CLI Docker commands to dive deep into debugging. Here are some helpful Docker commands you can run if you SSH into your Pi:
You can always find the compose file from OMV and run it from your CLI after SSHing into your Pi:
ssh user1@raspberrypi.local
sudo ls /ssdpool/data/docker/compose/<compose_file_name>
# e.g. compose_file_name == nextcloud
sudo cd /ssdpool/data/docker/compose/nextcloud/
docker compose up --built --wait
docker compose logs --follow
sudo docker stop $(sudo docker ps --format "{{.Names}}" | tr '\n' ' ')
sudo docker ps --filter "status=exited"
sudo docker container prune
sudo docker network rm nextcloud-aio
sudo docker volume ls --filter "dangling=true"
Caution: if you did not set your NEXTCLOUD_DATADIR to be outside your container, then this command will delete all your user data stored in the container! Only run this if you want a completely fresh start.)
sudo docker volume prune --filter all=1
sudo docker ps --format "{{.Names}}" | tr '\n' ' '
sudo docker volume rm <volume_name>
If you run this command:
docker compose up --build --wait
And get this error:
Error response from daemon: failed to create task for container: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: can't get final child's PID from pipe: EOF: unknown
Fix it by running:
# 1. List containers
docker ps
# 2. Stop all containers
sudo docker stop $(sudo docker ps --format "{{.Names}}" | tr '\n' ' ')
# 3. Note: `prune` will delete all your containers! Remember that if your NEXTCLOUD_DATADIR is set externally, then your user and account data won't be deleted.
docker container prune
# 4. Remove caddy volumes
docker volume rm caddy_certs caddy_config caddy_data
# 5. Run compose up from CLI, or just do it from the OMV UI.
sudo ls /ssdpool/data/docker/compose/<compose_file_name>
# e.g. compose_file_name == nextcloud
sudo cd /ssdpool/data/docker/compose/nextcloud/
docker compose up --build --wait
This may happen if you have a power outage and one of the SSDs is writing during that time period. To fix it, start a scrub by going to Storage > zfs > Pools > Select your ssd-pool > Tools > Scrub. You can check the status of the scrub from CLI:
# 1. SSH into pi
ssh user1@raspberrypi.local
# 2. Before scrub: check pool status
user1@raspberrypi:~ $ zpool status
pool: ssd-pool
state: DEGRADED
status: One or more devices could not be used because the label is missing or
invalid. Sufficient replicas exist for the pool to continue
functioning in a degraded state.
action: Replace the device using 'zpool replace'.
see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-4J
scan: scrub repaired 0B in 00:24:44 with 0 errors on Sun Jun 8 00:48:45 2025
config:
NAME STATE READ WRITE CKSUM
ssd-pool DEGRADED 0 0 0
mirror-0 DEGRADED 0 0 0
ata-TEAM_SSD_A ONLINE 0 0 0
1234567891234567890 UNAVAIL 0 0 0 was /dev/disk/by-id/ata-TEAM_SSD_B-part1
# 3. After scrub: check pool status
user1@raspberrypi:~ $ zpool status
pool: ssd-pool
state: ONLINE
status: One or more devices has experienced an unrecoverable error. An
attempt was made to correct the error. Applications are unaffected.
action: Determine if the device needs to be replaced, and clear the errors
using 'zpool clear' or replace the device with 'zpool replace'.
see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-9P
scan: scrub repaired 42.8G in 00:04:04 with 0 errors on Mon Jul 28 18:23:22 2025
config:
NAME STATE READ WRITE CKSUM
ssd-pool ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
ata-TEAM_SSD_A ONLINE 0 0 0
ata-TEAM_SSD_B ONLINE 0 0 364K
You may have to wait a few hours. Once this is done, the drive should come back online.
This may happen if a drive completely fails. In this case, you may need to RMA your failing drive. Once you have a replacement drive, shut down your server and attach the new drive.
Replace the drive in your ZFS pool using the following commands:
# 1. SSH into pi
ssh user1@raspberrypi.local
# 2. Before replacement: check pool status
user1@raspberrypi:~ $ zpool status
pool: ssd-pool
state: DEGRADED
status: One or more devices has been removed by the administrator.
Sufficient replicas exist for the pool to continue functioning in a
degraded state.
action: Online the device using zpool online' or replace the device with
'zpool replace'.
scan: scrub repaired 0B in 01:22:31 with 0 errors on Sun Mar 8 01:46:32 2026
config:
NAME STATE READ WRITE CKSUM
ssd-pool DEGRADED 0 0 0
mirror-0 DEGRADED 0 0 0
ata-TEAM_SSD_A REMOVED 0 0 0
ata-TEAM_SSD_B ONLINE 0 0 0
errors: No known data errors
# 3.1 Validate with lsblk which drive you just added
user1@raspberrypi:~ $ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sda 8:0 0 1.9T 0 disk
sdb 8:16 0 1.9T 0 disk
├─sdb1 8:17 0 1.9T 0 part
└─sdb9 8:25 0 8M 0 part
...
# 3.2 Use lsblk -f as sanity check on pool label on existing drive
user1@raspberrypi:~ $ lsblk -f
NAME FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
sda
sdb
├─sdb1 zfs_member 7000 ssd-pool 12345678987654321
└─sdb9
# 4. Replace drive with STATE: REMOVED
sudo zpool replace ssd-pool ata-TEAM_SSD_A /dev/sda
# 5. Check resilvering status
user1@raspberrypi:~ $ zpool status
pool: ssd-pool
state: DEGRADED
status: One or more devices is currently being resilvered. The pool will
continue to function, possibly in a degraded state.
action: Wait for the resilver to complete.
scan: resilver in progress since Fri Apr 17 21:51:03 2026
376G / 814G scanned at 41.8G/s, 291M / 814G issued at 32.4M/s
275M resilvered, 0.03% done, 07:09:03 to go
config:
NAME STATE READ WRITE CKSUM
ssd-pool DEGRADED 0 0 0
mirror-0 DEGRADED 0 0 0
replacing-0 DEGRADED 0 0 0
ata-TEAM_SSD_A REMOVED 0 0 0
sda ONLINE 0 0 0 (resilvering)
ata-TEAM_SSD_B ONLINE 0 0 0
errors: No known data errors
# 6. When complete, you should see the state restored to ONLINE
Thank you for reading until the end:)