Show pageOld revisionsBacklinksBack to top This page is read only. You can view the source, but not change it. Ask your administrator if you think this is wrong. <html><center><span style="color:#b7620b;font-size:300%;">Incomplete Document</span></center></html> <html><center><span style="color:#b7620b;font-size:300%;">Not for public use</span></center></html> {{ :underconstruction.jpg?400 |}} \\ ---- <html><center><span style="color:#000066;font-size:300%;">Docker in OMV 8</span></center></html> \\ [[omv7:docker_in_omv|{{ :omv7:dockeromv7-1.png?direct&600 |Docker in OMV 8}}]] ---- \\ \\ ====== Docker in OMV 8 ====== \\ \\ ===== Summary ===== \\ \\ [[https://forum.openmediavault.org/|{{ :omv7:dockeromv7-2.jpg?direct&200|Go to -> OMV forum}}]] **This document establishes a method to successfully install any application on OMV using Docker.** The [[https://forum.openmediavault.org/|OMV forum]] is a bi-directional tool. Provides users with solutions to their problems. It provides developers with information about user problems and allows them to implement appropriate solutions in software and methods. In the case of Docker, the forum has received numerous queries about very diverse problems. Based on that forum experience, this document offers a simple method for configuring Docker that fixes the vast majority of these problems before they arise. \\ \\ **Index:** * [[omv7:docker_in_omv#what_is_docker|What is Docker.]] * [[omv7:docker_in_omv#user_and_permission_management_in_docker_and_omv_more_security|User and permission management in docker and OMV. More security.]] * [[omv7:docker_in_omv#install_and_configure_docker|Install and configure Docker.]] * [[omv7:docker_in_omv#configuring_a_container_step_by_step_jellyfin|Configuring a container step by step (Jellyfin).]] * [[omv7:docker_in_omv#examples_of_configuration_of_some_containers|Examples of configuration of some containers.]] * [[omv7:docker_in_omv#some_basic_procedures_for_container_management|Some basic procedures for container management.]] * [[omv7:docker_in_omv#the_whys|The whys.]] \\ ---- \\ ===== What is Docker ===== \\ \\ [[https://www.docker.com/resources/what-container/|{{ :omv7:dockeromv7-3.png?direct&200|Go to -> www.docker.com}}]] //" A container is a standard unit of software that packages up code and all its dependencies so the application runs quickly and reliably from one computing environment to another. A Docker container image is a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries and settings.// //Container images become containers at runtime and in the case of Docker containers – images become containers when they run on Docker Engine. Available for both Linux and Windows-based applications, containerized software will always run the same, regardless of the infrastructure. Containers isolate software from its environment and ensure that it works uniformly despite differences for instance between development and staging. "// \\ \\ ** That's all very well, but... :-) What the hell is docker? :-) ** \\ \\ \\ [[omv7:docker_in_omv#what_is_docker|{{:omv7:dockeromv7-4.png?direct&300 |What?}}]] That definition is very good and very professional, but it is of little use to people on the street, so we will try to explain in an easier way what docker is and how it works. **If you are an experienced docker user you will probably want to skip this part**. If this is your first time using docker, keep reading. Docker is a system that allows you to run an application within your server as if it were an independent and isolated system. It has its own processes, its own file system, and its own networking, all independent of the main (host) server. The container cannot access the host's file systems or network, and vice versa. This is why we say that it is isolated, and way it cannot damage your system. It is safe. Docker is quite similar to a virtual machine, but with one key difference: a virtual machine includes a complete operating system, with its own kernel, drivers, and services. Docker, on the other hand, does **not** run a complete operating system. Instead, it uses the host's kernel and only isolates the container's processes, network, and file system. Because of this, containers consume far fewer resources and start up in a matter of seconds, although their isolation is not as deep as that of a traditional virtual machine. **This makes containers designed for different architectures**. A container designed for Raspberry PI (ARM architecture) will not work on an Intel/AMD system (amd64 architecture) and vice versa. You should keep this in mind when choosing a container to install on your system. Many modern containers include multiple architectures, and Docker will automatically select the correct one. However, not all images support this, so it is important to verify that the image is compatible with your architecture. At this point it is good to remember that the **32-bit architecture is obsolete**, little by little 32-bit containers are disappearing. OMV 8 no longer works on 32-bit systems, so **if you were able to install OMV 8, your system is 64-bit**. When installing a container, always choose the 64-bit version. Docker will usually select the correct image automatically, but if multiple variants exist, choose the one labeled amd64 or x86_64. [[omv7:docker_in_omv#what_is_docker|{{ :omv7:dockeromv7-17.jpg?direct&200|32-bits}}]] The operation of docker is very simple. Someone on the Internet packages a system into a file we call an **image**. This image contains the necessary packages for the application we want to use to work. Docker downloads that image, installs it on our server and runs it. We already have a **container** working. Now the creator of that image does the corresponding maintenance and publishes a new updated image. Docker can download and apply the new image if you trigger an update. Tools like docker-compose make this easy by pulling the updated image and restarting the container. This way, your container can be kept up to date. So far so good. But now we want to configure certain information in our application, for example a password to access that application. We could "enter" the container and make that configuration by writing to the ''/folderpass/password'' file inside the container. That would work, but on the next image update that ''/folderpass/password'' file will be overwritten and the settings will be lost. To solve this Docker allows **folder mapping**. Mapping a folder means that Docker will make a configuration such that when the container writes to the ''/folderpass/password'' file it will actually be writing to an external folder, a folder located on our server file system. This way, when we update the container image, all its files will be overwritten except ''/folder/password'', since this folder is not in the container but in the file system of the host server, and when the container is running it will be able to continue reading the password that we have stored in our server file system. As an added bonus, mapping a folder makes it easier to manipulate the files in that folder from the server without needing to enter the container. In the same way that Docker maps folders it can also **map network ports**, we can map port 3800 that the container uses internally to any port on our server, for example 4100, the container will send data packets to port 3800 internally but Docker will that these packets be sent through port 4100 of our server. [[omv7:docker_in_omv#what_is_docker|{{:omv7:dockeromv7-18.jpg?direct&300 |PUID Explained}}]] **We can also map users**. And this is important to understand. The container will work internally as //root//, but we can make that user be another user on the server, for example the user //superman//. From that moment on, everything the container does to the mapped files or ports will not be done by //root//, it will be done by //superman//. That allows us to restrict the permissions of that container, we only have to restrict the permissions of the //superman// user of our system. We will give the user //superman// write permissions to the ''/folderpass/password'' file on our system so that he can write or modify that file but we will not give him permissions to write to any other folders. In this way we ensure that the container remains isolated. To define all these container configurations the **openmediavault-compose plugin uses docker-compose** for its simplicity. Using a configuration file of a few lines we define the mappings and other configurations of a container and then we execute it. To map a user we define the PUID value, the user's identifier, and to map the group it will be the PGID value, the group's identifier. In the OMV GUI we can see the PUID value for each user in the USERS > USERS tab by opening the UID and GID columns using the icon at the top right. So if the user //superman// has the values 1004 and 100, in the compose file we would do something like this: ''- PUID=1004'' ''- PGID=100'' The way to map a folder (**volume**) in docker-compose is something like this: ''- /srv/dev-disk-by-uuid-9d43cda9-20e5-474f-b38b-6b2b6c03211a/appdata/folderpass:/folderpass'' That could be one of the lines in the compose file that defines a container. This line is divided into two parts. To the left of the '':'' we have ''/srv/dev-disk-by-uuid-9d43cda9-20e5-474f-b38b-6b2b6c03211a/appdata/folderpass'' which is the path of a real folder on our server, in our file system. On the right of the '':'' we have ''/folderpass'' which is the path of a folder within the container, in its own file system. <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> All that long string of numbers is the path of one of our disks on the server and inside that disk we have an appdata folder and inside we create the folderpass folder<br> Filesystem mount paths are usually in the /srv folder and the following folder contains a uuid to uniquely identify that drive. That folder is the mount folder for that hard drive. You should never modify the permissions of that folder or use it to create a shared folder. Create a folder inside to use as a shared folder. </tr></table></body></html> From now on, every time the //root// user of the container writes to its ''/folderpass'' folder, what will really be happening is that the //superman// user will be writing to our ''/srv/dev-disk-by-uuid-9d43cda9-20e5-474f-b38b-6b2b6c03211a/appdata/folderpass'' folder from our server. The content of that folder is what we call **persistent data**. Naturally we must give permission to the //superman// user on our server to write to that folder or the container will throw an error. The advantage of all this is that the container is limited to writing to that folder. We could be unlucky enough to download an image that has harmful code inside. Or if that container was exposed to the internet and had a security hole, perhaps it could be hacked. In this case the hacker could take control of the //root// user of the container. The advantage is that that //root// user on our system is actually the //superman// user, and in this case, no matter how superman he is, he can only write to the ''/folderpass'' folder, so he cannot access our server in any way. The user //superman// does not have permissions to write or read any other files on our server. Contained threat. Conclusion. **Never map the container user to the //root// user of the server**, unless it is absolutely necessary and the container developer is fully trusted. If there were a security hole in that container, your server would be at the mercy of the hacker, since they would have permissions for everything. Related to this, **never include the user running a container in the //docker// group**, this is another story but any user in the docker group can gain //root// access to the system by escalating permissions. If you're reading this, it's probably your first time using Docker. Docker may seem complicated at first glance, but once you get over the initial learning curve, setting up and installing a container literally takes less than 30 seconds. Keep going. ---- \\ ===== User and permission management in Docker and OMV. Improved security. ===== \\ \\ <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Note </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> You should read this even if you are an experienced Docker user but have no experience with OMV. </tr></table></body></html> \\ [[omv7:docker_in_omv##user_and_permission_management_in_docker_and_omv_more_security|{{:omv7:dockeromv7-5.jpg?direct&400 |Hacker}}]] Docker’s security model is based on a simple principle: **a container has exactly the same permissions as the user (UID/GID) it is executed with.** The easiest way to manage this in OMV is to create a dedicated user —for example, //appuser//— and use it to run all your containers. Simply grant it read/write access only to the folders required by your containers. This prevents containers from accessing other parts of the system and provides a solid level of security. === How OMV manages users and shared folders === In OMV, all users created through the GUI belong by default to the primary group users (GID=100). Shared folders are also created with ownership set to ''root:users'' (user=''root'', group=''users''), with read/write permissions for both. This is the standard Linux filesystem permission layer. This means that any user created from the GUI automatically has read/write access to any shared folder, since they belong to the //users// group —unless you explicitly restrict it using OMV’s permission management, which is a higher-level Samba layer. OMV applies a Samba permission layer on top of the filesystem permissions. These are the permissions you manage from the OMV GUI. * Samba permissions can restrict —but never expand— filesystem permissions. * ACLs are unnecessary in 99% of cases; avoid them unless you know exactly what you're doing. === Implications for Docker === If you use a single user like //appuser//, it will have access to all the folders you allowed through the GUI. This works well for most containers, but not for every scenario. For example, if the Jellyfin container should only access ''/Media'', but Nextcloud requires ''/Documents'' as well, both containers would need to run under a user with access to ''/Documents''. This may not be desirable if one of the containers is exposed to the Internet. === User isolation (optional, for those who need it) === If you need maximum separation, you can create one user per container —but this only works properly if you avoid the //users// group (GID=100). To do so, you must create the users from the CLI: ''sudo useradd -U jellyfin'' This creates: * a user //jellyfin// * a primary group //jellyfin// When you return to the GUI, you will see the user and can manually add it to its own group. This way, all files created by the container will belong to the //jellyfin// user/group, and no other container will be able to access them unless you explicitly add that container’s user to the group. You also retain full control to grant permissions to your shared folders individually. === Practical conclusion === **In this document, we will use a single //appuser// created from the GUI. This is sufficient for 99% of users.** If your use case requires stronger isolation between containers, apply the techniques described to create users outside of GID=100 and assign them individually. ---- \\ {{ :divider2.png?nolink&800 |}} ===== Install and configure Docker ===== \\ \\ ==== 1. Installation ==== {{ :omv7:dockeromv7-7.jpg?direct&1200 |Expand image -> Installation}} In OMV8's GUI:\\ Under **SYSTEM > OMV-EXTRAS**, click the **DOCKER REPO** button and then click **SAVE**. This activates the Docker repository so you can install Docker and the Compose plugin.\\ Next, go to **SYSTEM > PLUGINS**, find and select **openmediavault-compose 8.X**, and click **INSTALL**. * Installing the openmediavault-compose plugin will also install the openmediavault-sharerootfs plugin as a dependency. \\ <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#FFB663;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Warning </span></strong></td></tr><tr><td style="background-color:#FFE4A6;height:25px;width:380px;"> Do not uninstall the openmediavault-sharerootfs plugin. It is a dependency of the openmediavault-compose. Uninstalling openmediavault-sharerootfs while openmediavault-compose is installed will also remove the openmediavault-compose plugin. </tr></table></body></html> ---- ==== 2. Plugin Settings ==== The first step is to define the folders where the different data is stored. To do this, we go to **SERVICES > COMPOSE > SETTINGS**. There are many possible NAS layouts. First we will look a simple setup, and then a more advanced configuration. <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> Installing OMV on a USB flash drive may seem unusual to newcomers, but you may be surprised to learn that many professional-grade servers boot directly from USB devices.<br> This approach provides several advantages and no drawbacks:<br> <ul> <li><b>System performance is unaffected</b>, since almost all operations run in RAM.</li> <li>The <b>openmediavault-writecache plugin</b> protects the USB drive by minimizing unnecessary write operations, greatly extending its lifespan.</li> <li><b>Backups become extremely simple</b>: just create an image of the USB drive using <i>usbimager</i> on your PC.</li> <li><b>Restoring the system is even easier</b>: clone the image onto a new USB drive in minutes.</li> <li><b>Docker benefits</b> because the SSD/NVMe storage remains free for containers, where high speed actually matters.</li> <li>Using a USB drive also frees valuable <b>SATA or NVMe ports</b> on the motherboard.</li> </ul> If your system is already installed on a disk, you can easily migrate OMV to a USB flash drive using <b>omv-regen</b>. </tr></table></body></html> ---- === 2.1 SIMPLE OMV NAS SYSTEM === In this simple setup, the OMV operating system runs from a USB stick, and there is a single data drive that stores all NAS data. On this drive we will configure Docker and all related folders. The following diagram shows a schematic example of this layout: {{ :omv7:dockeromv7-28.jpg?direct&1200 |Expand image -> Docker folders - Simple NAS}} In this case, all required folders are located on the same drive, which makes the configuration very straightforward. All folders will live under the mount point of that drive, for example: ''/srv/dev-disk-by-uuid.../appdata'' Create these shared folders from the OMV GUI, then follow the explanations in section 2.3. Just keep in mind that, in this simple scenario, all paths will be inside the same mount folder—for example: * ''/srv/dev-disk-by-uuid-…/docker'' * ''/srv/dev-disk-by-uuid-…/backup_compose'' * ... Since there is only one data drive, everything lives under the same location. ---- === 2.2 ADVANCED OMV NAS SYSTEM === In more advanced setups, your system may look similar to the following example. The diagram below represents a typical OMV NAS layout. From this point onward, all explanations in the document will be based on this example system. Your own system will probably differ — simply adapt the configuration logic to match your real setup. * The OMV operating system is installed on a USB flash drive. * A mergerfs pool composed of three hard drives stores users’ large NAS data. * A separate hard drive stores NAS backup data. * A high-speed NVMe drive is used for Docker data. On the right side, you can see how the plugin’s SETTINGS tab may look after applying this configuration. If your system is simpler or more complex, adjust the folder paths accordingly. {{ :omv7:dockeromv7-6.png?direct&1400 |Expand image -> Docker folders - Typical NAS}} ---- === 2.3 CONFIGURATION === In any case, the main recommendation here is to **keep Docker data separate from the OMV operating system**. **You can name these folders however you prefer.** In this document we use the names shown in the diagrams for clarity. The example folders are: * ''appdata'' * ''data'' * ''backup_compose'' * ''docker'' (These names match the diagrams in this document. You can create the shared folder with any name you prefer; the plugin will work the same.) We will review them one by one below. <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;padding:10px;width:380px;"> <b>Why you should keep Docker off the OS drive</b> (this may surprise Windows users — Linux handles storage differently):<br> <ul> <li>If Docker lives on the same disk as the OS, a reinstallation of OMV will remove Docker data. Keeping Docker data on a separate drive makes recovery as simple as remounting that drive.</li> <li>Installing Docker on the OS disk can fill the root filesystem (rootfs). Depending on the number and type of containers, you can run out of rootfs space and cause system problems.</li> <li>Placing Docker on a faster drive (SSD or NVMe) improves container performance. OMV itself can run well from a USB flash drive (use <i>openmediavault-writecache</i>), while Docker benefits from high-speed storage.</li> <li>Do not put Docker on the OMV USB flash drive: Docker does continuous writes and will reduce the flash drive’s lifespan even with write caching enabled.</li> <li>If you don't have a fast spare drive, putting Docker on one of your data drives is better than keeping it on the OS disk — performance is lower, but rootfs remains protected.</li> </ul> </td></tr></table></body></html> \\ <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;padding:10px;width:380px;"> <b>Recommended capacity for the Docker drive</b><br> Required size depends on the number and type of containers. A sensible minimum is **60–100 GB**. If you run media servers (Jellyfin, Plex) with very large libraries, consider **250–500 GB** or more. Nextcloud can also require significant storage depending on your usage. </td></tr></table></body></html> \\ <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Note </span></strong></td></tr><tr><td style="background-color:#E6FEFF;padding:10px;width:380px;"> If your NAS already contains data, you probably already have a data folder (it may use a different name). Select that folder in the plugin settings if appropriate. </td></tr></table></body></html> \\ == 2.3.1 appdata folder == ''appdata'' (This name matches the diagrams in this document. You can create the shared folder with any name you prefer; the plugin will work the same.) \\ * WHAT IS APPDATA FOLDER: * The plugin creates the //appdata// folder to store copies of the compose files it generates (both the .yml file and the .env file). These copies act as backups — do not edit them manually (always use the plugin GUI). * The plugin also generates a global.env file in the appdata root. Do not edit this file manually — it is automatically overwritten. * The procedure expected by the plugin would be to create a second folder in which to store persistent container data. * However, many users — including myself — also use this folder to store persistent container data. It works perfectly //as long as you create a dedicated subfolder// inside each container folder. * This is important because the plugin sets special permissions on every container directory it creates. If you mount a Docker volume directly into one of those folders, Docker may change the permissions and break the container or the plugin. Creating a subfolder prevents this and keeps everything safe. * If you prefer to separate the persistent data into another folder, you can do so without any problem. In this document we will place persistent data in subfolders within //appdata//. *...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> Each container is defined by two files: the <i>yml</i> (service definitions: image, volumes, ports, etc.) and the <i>env</i> (environment variables for that container). You do not need to edit the env file manually for common tasks. </tr></table></body></html> \\ * HOW TO CONFIGURE THE APPDATA FOLDER: * In the plugin’s SETTINGS → COMPOSE FILES section, click the **+** button next to the SHARED FOLDER field to create a new shared folder, or select an existing one if you already have it. * If creating a new folder: * NAME: <i>appdata</i> * FILE SYSTEM: choose the SSD/NVMe or drive you want to use * Click <b>SAVE</b> * Once selected, click **SAVE** in the plugin settings to apply the choice. *...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> This folder will store the persistent data of each container. Example for Jellyfin config:<br> <b>/srv/dev-disk-by-uuid-.../appdata/jellyfin/config:/config</b><br> When the container starts, Docker will create missing subfolders (for example, <i>jellyfin</i> and <i>config</i>) automatically.<br> Don't worry for now, we'll see it with examples later. </tr></table></body></html> *...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#FFB663;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Warning </span></strong></td></tr><tr><td style="background-color:#FFE4A6;height:25px;width:380px;"> Always create a subfolder inside each container directory in <i>appdata</i> to store persistent data.<br><br> ❌ <b>Do NOT do this:</b><br> /srv/.../appdata/jellyfin:/config<br> → Docker writes directly inside <i>appdata/jellyfin</i> and modifies its permissions.<br><br> ✔️ <b>Do this instead:</b><br> /srv/.../appdata/jellyfin/config:/config<br> → Keeps data isolated and preserves the plugin’s permissions. </tr></table></body></html> *...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#2C6700;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Advanced configuration. </span></strong></td></tr><tr><td style="background-color:#e7fae1;height:25px;width:380px;"> The compose plugin supports <b>relative paths</b> in volume definitions. Using relative paths ensures the data is stored in the correct subfolder.<br><br> Example for Jellyfin:<br> <b>- ./config:/config</b><br> This creates:<br> <b>/appdata/jellyfin/config</b><br><br> The plugin also supports <b>symlinks</b> to define volume paths. You can create them with the <i>openmediavault-symlinks</i> plugin or manually. Both systems (relative paths and symlinks) can be combined. </tr></table></body></html> \\ == 2.3.2 data folder == ''data'' (This name matches the diagrams in this document. You can create the shared folder with any name you prefer; the plugin will work the same.) \\ * WHAT IS DATA FOLDER: * The data folder is basically a shortcut to a shared folder that can be used in compose files to define the location of a container volume. * When a container is started, the ''CHANGE_TO_COMPOSE_DATA_PATH'' variable defined in the compose file is automatically replaced with the shared folder you configure here. * You must configure this folder if you plan to use the plugin’s example compose files, as they rely on this variable. * You can choose any shared folder for this purpose; it does not need to be named "data". \\ * HOW TO CONFIGURE THE DATA FOLDER: * In the plugin configuration, under the DATA section, select the shared folder you want to assign to CHANGE_TO_COMPOSE_DATA_PATH. * Click SAVE. \\ == 2.3.3 backup_compose folder == ''backup_compose'' (This name matches the diagrams in this document. You can create the shared folder with any name you prefer; the plugin will work the same.) \\ * WHAT IS BACKUP_COMPOSE FOLDER: * The compose plugin allows you to schedule automatic backups of each container’s persistent data. * All scheduled backups created by the plugin are stored in this folder. \\ * HOW TO CONFIGURE THE BACKUP_COMPOSE FOLDER: * Create the //backup_compose// shared folder in the OMV GUI and select it in the compose plugin settings in the BACKUP section. *...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Note </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> In the system diagram of this document, the <i>backup_compose</i> folder is located on the NVMe drive rather than on the dedicated backup drive.<br><br> The plugin’s scheduled backup function is designed to produce a consistent and up-to-date copy of your persistent data by temporarily stopping the containers during the backup and starting them again afterward. Since each backup overwrites the previous one, this folder is ideal for use together with a separate backup application, which can then create versioned and/or compressed backups <b>without needing to stop the containers</b>.<br><br> If you prefer, you can place this folder directly on your backup drive instead — both approaches are valid. </tr></table></body></html> \\ == 2.3.4 docker folder == ''docker'' (This name matches the diagrams in this document. You can create the shared folder with any name you prefer; the plugin will work the same.) \\ * WHAT IS DOCKER FOLDER: * This folder contains Docker’s internal data: downloaded images, layer data, and various runtime metadata required for Docker to operate. * Under normal circumstances, **there is no need to preserve** the contents of this folder across a reinstallation of OMV. Everything stored here can be automatically recreated or re-downloaded when containers start. * However, it is strongly recommended to **keep this folder off the root filesystem** (/) to avoid filling up the OS drive and to prevent performance issues. * The compose plugin allows you to relocate this folder easily. In the plugin settings, under the DOCKER section, set the DOCKER STORAGE field to the new path. By default, Docker uses /var/lib/docker on the OMV root filesystem, which you should normally change. * When defining this path, always use the **full absolute path**. Avoid symlinks — they can cause unexpected issues with Docker. \\ * HOW TO CONFIGURE THE DOCKER FOLDER: * Create a shared folder named //docker// in the OMV GUI. * Go to STORAGE → SHARED FOLDERS and locate the absolute path of the docker shared folder in the ABSOLUTE PATH column (expand the column if needed using the icon in the top-right corner). * Click the copy icon next to the path and paste it into the corresponding field in the compose plugin settings. *...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#FFB663;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Warning </span></strong></td></tr><tr><td style="background-color:#FFE4A6;height:25px;width:380px;"> <b>The filesystem hosting the docker folder should preferably be EXT4</b>.<br> If you need to place it on <b>ZFS</b> or <b>BTRFS</b> file system, consult the official Docker documentation for the required configuration.<br> <b>Never use an NTFS</b> file system for Docker data — it does not work and will lead to failures. (or generally for anything on linux other than a staging mount for copying data).<br> Do not place the Docker folder in a <b>mergerfs</b> pool, as Docker will spread its internal files across multiple drives, eventually causing corruption or operational problems.<br> If the only storage available is inside a mergerfs pool, you may: - Create the Docker folder directly on a specific drive that belongs to the pool instead of inside the pool, and configure the plugin using the absolute path of that drive. - Avoid using mergerfs rebalance on that pool, as it may move Docker’s files to another drive and break Docker. </tr></table></body></html> *...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#2C6700;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Advanced configuration. </span></strong></td></tr><tr><td style="background-color:#e7fae1;height:25px;width:380px;"> You can <b>manually edit /etc/docker/daemon.json to customize</b> Docker’s behaviour.<br> This is required, for example, to configure an NVIDIA GPU driver or to set a custom storage driver for certain filesystems.<br> If you need to customize this file, simply <b>leave the Docker storage field empty</b> in the plugin settings — the plugin will not modify the file.<br> </tr></table></body></html> ---- === 3. Create appuser === If you read the introduction, you already know whether the //appuser// user is sufficient for your needs or if you should create a custom user. If you are happy with this user for some or all containers, proceed; otherwise, customize it as explained earlier. {{ :omv7:dockeromv7-8.jpg?direct&400|Expand image -> UID-GID}} * In the OMV GUI create a user called //appuser//. * Add //appuser// to the groups you need. * For example, if you plan to use hardware transcoding with an Intel GPU, add this user to the ''render'' and ''video'' groups. * ...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#FFB663;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Warning </span></strong></td></tr><tr><td style="background-color:#FFE4A6;height:25px;width:380px;"> Do not add <i>appuser</i> to the <i>docker</i> group. This creates a security hole. </tr></table></body></html> * Edit //appuser//'s permissions and grant appropriate access to each shared folder that containers will need: * Give write access to //appdata// (for persistent container configuration). * Give access only to the folders required as container volumes (e.g., ''/media'' for Jellyfin movies). * ...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> <br> To create the <i>appuser</i> user in the OMV GUI:<br> - Go to the USERS > USERS tab and press the +CREATE button.<br> - In the NAME field type <i>appuser</i><br> - In the PASSWORD field define a strong password and confirm it.<br> - If needed, add <i>appuser</i> to the required groups by clicking the GROUPS field.<br> - Click SAVE.<br><br> To assign permissions to <i>appuser</i>, select <i>appuser</i> and press the SHARED FOLDER PERMISSIONS button.<br> - For each folder, choose the appropriate permissions and ensure the box is highlighted in yellow.<br> - Click SAVE.<br> <br> </tr></table></body></html> * Open the UID and GID columns and note the values for //appuser//: * Example: UID=1002 GID=100 * If you already have one user, //appuser// UID will be 1001; with two users, UID=1002, etc. This may vary depending on your system. <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#FFB663;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Warning </span></strong></td></tr><tr><td style="background-color:#FFE4A6;height:25px;width:380px;"> Except in very controlled special cases, <b>never</b> assign the admin user (UID=998) or root (UID=0) to manage a container. This is a <b>serious security flaw</b>.<br> Doing so gives the container unrestricted access to your system. Consider carefully what the container is capable of doing before assigning elevated privileges. </tr></table></body></html> ---- === 4. Global environmental variables === Global environment variables will be used in the procedure that follows this document. The plugin allows you to define global environment variables in a file that will be available to all running containers. This means that the variables defined in this file can be used in the different compose files. When you start a container, docker will replace those variables with their real values. This is very useful for defining paths to folders or the user running the container. We define these values once and have them updated automatically in all containers. Example: {{ :omv7:dockeromv7-9.jpg?direct&400|Expand image -> OMV System}} * Variables defined in the global variables file: <code> # THE FOLLOWING VARIABLES CAN BE USED IN ANY COMPOSE FILE # THEIR VALUE WILL BE REPLACED BY THE VALUE ASSIGNED HERE # YOU CAN ADD AS MANY VARIABLES AS YOU NEED # # # Customize the PID and GID value of your appuser user. # You can see it in the OMV GUI in the USERS > USERS tab # APPUSER_PUID=1002 APPUSER_PGID=100 # # Customize your time zone value. # You can see it in the OMV GUI in the SYSTEM > DATE & TIME tab # TIME_ZONE_VALUE=Europe/Madrid # # In this file you can define how many paths you need for different compose files. # The following are examples corresponding to the example system in the Docker on OMV document on the omv-extras wiki. # Customize the values according to your system configuration. # PATH_TO_APPDATA=/srv/dev-disk-by-uuid-9d43cda9-20e5-474f-b38b-6b2b6c03211a/appdata PATH_TO_DATA=/srv/mergerfs/pool/data PATH_TO_DOCUMENTS=/srv/mergerfs/pool/data/documents PATH_TO_MEDIA=/srv/mergerfs/pool/data/media PATH_TO_MOVIES=/srv/mergerfs/pool/data/media/movies PATH_TO_PHOTOS=/srv/mergerfs/pool/data/media/photos PATH_TO_BACKUPS=/srv/dev-disk-by-uuid-384444bb-f020-4492-acd2-5997e908f49f/backups PATH_TO_DOWNLOADS=/srv/dev-disk-by-uuid-384444bb-f020-4492-acd2-5997e908f49f/downloads </code> * Now we could define the following compose file: <code> --- services: jellyfin: image: lscr.io/linuxserver/jellyfin:latest container_name: jellyfin environment: - PUID=${APPUSER_PUID} - PGID=${APPUSER_PGID} - TZ=${TIME_ZONE_VALUE} volumes: - ${PATH_TO_APPDATA}/jellyfin/config:/config - ${PATH_TO_APPDATA}/jellyfin/cache:/cache - ${PATH_TO_MEDIA}:/media ports: - 8096:8096 restart: unless-stopped </code> * And the file that would actually be executed would be this: <code> --- services: jellyfin: image: lscr.io/linuxserver/jellyfin:latest container_name: jellyfin environment: - PUID=1002 - PGID=100 - TZ=Europe/Madrid volumes: - /srv/dev-disk-by-uuid-9d43cda9-20e5-474f-b38b-6b2b6c03211a/appdata/jellyfin/config:/config - /srv/dev-disk-by-uuid-9d43cda9-20e5-474f-b38b-6b2b6c03211a/appdata/jellyfin/cache:/cache - /srv/mergerfs/pool/data/media:/media ports: - 8096:8096 restart: unless-stopped </code> What this variable system does is convert the variables that we have defined in the compose file into the actual values that we need to define in the compose file. If we are only creating one compose file it may not make much sense, but it is very useful when there are several compose files. The advantage of using this system is that we define the compose file once and never need to modify it. Even if we reinstall the OMV system we just need to update those global variables and all our containers will be up to date and continue to work as before. \\ <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Note </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> At this time, the plugin fully supports global environment variables, even while using the plugin's backup utility, so there is no reason to use symlinks. But if you want to use them for some reason, you can still do so.<br> If you don't want to use environment variables, simply write the full values to the compose files without using variables. </tr></table></body></html> \\ * In the OMV GUI go to SERVICES > COMPOSE > FILES and press the EDIT GLOBAL ENVIRONEMENT FILE button. * In the GLOBAL ENVIRONMENT field copy and paste the example file at the beginning of this section. * Customize it with your own values. * PUID and PGID -> You can see the values of user //appuser// in USERS > USERS in the UID and GID columns. See point 3 of this document. * TZ -> You can see your local configuration in SYSTEM > DATE & TIME and in TIME ZONE field. * APPDATA -> Find the absolute path of your //appdata// shared folder in STORAGE > SHARED FOLDERS in the ABSOLUTE PATH column. Copy by clicking the link and paste it here. * Customize the values of all paths according to your system configuration. If you need to define other different paths you can also do it here in the same way as the rest of the variables. * You can define other variables according to your needs, such as database passwords or any value your compose file needs. ---- \\ {{ :divider2.png?nolink&800 |}} ===== Configuring a container step by step (Jellyfin) ===== \\ \\ === Choose a container === \\ [[https://hub.docker.com/|{{ :omv7:dockeromv7-10.png?direct&200|Go to -> https://hub.docker.com/}}]] * On the [[https://hub.docker.com/|dockerhub]] there are thousands of containers ready to configure. * Try to choose containers from reputable publishers ([[https://www.linuxserver.io/|linuxserver]] is very popular) or with many downloads and current ones. * Check that the container is compatible with your server's architecture amd64, arm64... * When choosing one read the publisher's recommendations before installing it. [[https://www.linuxserver.io/|{{ :omv7:dockeromv7-11.png?direct&200|Go to -> https://www.linuxserver.io/}}]] * The plugin has examples that you can install directly. As an example we are going to install [[https://jellyfin.org/|Jellyfin]]. *...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Note </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> If you have configured folders in the plugin's SETTINGS tab, the example files will usually work as is, but you may want to modify them to optimize your settings. If you finish reading this document and look at any of the example files, you will understand why. </tr></table></body></html> * Go to SERVICES > COMPOSE > FILES and click ADD button, then click ADD FROM EXAMPLE button. * Click on the EXAMPLE field and in the list look for the jellyfin file and select it. * In the NAMEame field you can simply write //jellyfin// * In the DESCRIPTION field you can write something to identify it as Media server. * Press SAVE button. * In the form you will see a line with the compose file you just added called //jellyfin//. Select that file and press the EDIT button. At the time of writing this document, the example compose file looks like this: <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> You will find containers where the creator has not published a compose file. Containers can be run from the CLI with a command line. The plugin uses docker-compose for easy setup but you need that compose file. If you can't find it, you can build it yourself using Composerize and starting from that container's docker command. There is a prepared Composerize container in the plugin's examples list. </tr></table></body></html> [[https://jellyfin.org/|{{ :omv7:dockeromv7-12.png?200|Go to -> https://jellyfin.org/}}]] <code> # https://hub.docker.com/r/linuxserver/jellyfin services: jellyfin: image: lscr.io/linuxserver/jellyfin:latest container_name: jellyfin environment: - PUID=1000 - PGID=1000 - TZ=Etc/UTC - JELLYFIN_PublishedServerUrl=192.168.0.5 #optional volumes: - CHANGE_TO_COMPOSE_DATA_PATH/jellyfin/library:/config - CHANGE_TO_COMPOSE_DATA_PATH/jellyfin/tvseries:/data/tvshows - CHANGE_TO_COMPOSE_DATA_PATH/jellyfin/movies:/data/movies ports: - 8096:8096 - 8920:8920 #optional - 7359:7359/udp #optional - 1900:1900/udp #optional restart: unless-stopped </code> <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Note </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> Verify on the official page that this compose file has not changed before installing it </tr></table></body></html> \\ <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> WHAT A COMPOSE FILE IS LIKE<br> A compose file is a yaml format file that is used to define the configurations that docker will adjust to the downloaded image to form the container.<br> The parts of this composition file for jellyfin are as follows:<br> <b>services:</b> It is always the first line and gives way to the definition of the different services.<br> <b>jellyfin:</b> Is the name of a service in this compose file. In this case there is only one but there could be more.<br> <b>image:</b> Defines where the container is downloaded from, in this case linuxserver. This value may have options to download different versions of images. In this case the word <b>latest</b> indicates that the latest available version will always be downloaded.<br> <b>container_name:</b> Simply the name of the container for this service, in this case jellyfin.<br> <b>environment</b> In this section some environment values are defined, such as in this case the user who will run the container, the time zone, or others.<br> <b>volumes</b> Here we define the folder mounts in the container.<br> <b>ports</b> To define port mounts.<br> <b>restart</b> Tells docker how we want the container to behave when the server starts, etc. In this case, the container will always be running unless we stop it manually.<br> </tr></table></body></html> ---- === Customize the compose file === \\ {{ :omv7:dockeromv7-9.jpg?direct&600|Expand image -> OMV System}} The next thing we need to do is adapt the container configurations so that it works on our system. We will see it below step by step. The first line contains a link to the website of the container developer, useful to reach quickly and take a look. It is always a good idea to read the editor's comments to familiarize yourself with the options and check that the container is valid for your architecture (x86, arm...) or see special configurations that we may need. To customize this compose file we will follow the system example used in the previous point of this document and the generated environmental global variables file, which is as follows: \\ \\ <code> # THE FOLLOWING VARIABLES CAN BE USED IN ANY COMPOSE FILE # THEIR VALUE WILL BE REPLACED BY THE VALUE ASSIGNED HERE # YOU CAN ADD AS MANY VARIABLES AS YOU NEED # # # Customize the PID and GID value of your appuser user. # You can see it in the OMV GUI in the USERS > USERS tab # APPUSER_PUID=1002 APPUSER_PGID=100 # # Customize your time zone value. # You can see it in the OMV GUI in the SYSTEM > DATE & TIME tab # TIME_ZONE_VALUE=Europe/Madrid # # In this file you can define how many paths you need for different compose files. # The following are examples corresponding to the example system in the Docker on OMV document on the omv-extras wiki. # PATH_TO_APPDATA=/srv/dev-disk-by-uuid-9d43cda9-20e5-474f-b38b-6b2b6c03211a/appdata PATH_TO_DATA=/srv/mergerfs/pool/data PATH_TO_DOCUMENTS=/srv/mergerfs/pool/data/documents PATH_TO_MEDIA=/srv/mergerfs/pool/data/media PATH_TO_MOVIES=/srv/mergerfs/pool/data/media/movies PATH_TO_PHOTOS=/srv/mergerfs/pool/data/media/photos PATH_TO_BACKUPS=/srv/dev-disk-by-uuid-384444bb-f020-4492-acd2-5997e908f49f/backups PATH_TO_DOWNLOADS=/srv/dev-disk-by-uuid-384444bb-f020-4492-acd2-5997e908f49f/downloads </code> \\ * We adjust the compose file as follows: \\ <html><body><pre><code> # https://hub.docker.com/r/linuxserver/jellyfin services: jellyfin: image: lscr.io/linuxserver/jellyfin:latest container_name: jellyfin environment: - PUID=${APPUSER_PUID} <FONT COLOR="red"># See Comment 1</FONT> - PGID=${APPUSER_PGID} <FONT COLOR="red"># See Comment 1</FONT> - TZ=${TIME_ZONE_VALUE} <FONT COLOR="red"># See Comment 2</FONT> #- JELLYFIN_PublishedServerUrl=192.168.0.5 <FONT COLOR="red"># See Comment 3</FONT> volumes: - ${PATH_TO_APPDATA}/jellyfin/config:/config <FONT COLOR="red"># See Comment 4</FONT> - CHANGE_TO_COMPOSE_DATA_PATH/media:/media <FONT COLOR="red"># See Comment 4</FONT> devices: <FONT COLOR="red"># See Comment 5</FONT> - /dev/dri:/dev/dri <FONT COLOR="red"># See Comment 5</FONT> ports: - 8096:8096 <FONT COLOR="red"># See Comment 6</FONT> restart: unless-stopped </code></pre></body></html> ---- <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#FFB663;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Warning </span></strong></td></tr><tr><td style="background-color:#FFE4A6;height:25px;width:380px;"> This file is in Yaml format, <b>indentations are important</b>. If you do not respect the indentations, Docker will not be able to interpret the configuration file and the container will give an error and will not start.<br> Whenever you ask for help on the forum, <b>post the compose file in a code box</b> to show the indentations so you can get help. To create a code box in the forum press the corresponding button in the toolbar. Hides sensitive data, such as passwords, email addresses, etc. </tr></table></body></html> == Environment == {{ :omv7:dockeromv7-19.jpg?direct&400|Expand image -> Users}} * <html><FONT COLOR="red">Comment 1:</FONT></html> * PUID and PGID correspond to the UID and GID values of the user who is going to manage the container on the system. * In our case we want this user to be //appuser//, so we enter the values of this user. * In the OMV GUI, go to the USERS > USERS tab and open the UID and GID columns using the icon at the top right. In these columns the values for the //appuser// user will be displayed. Let's assume that in this case they are UID=1002 and GID=100. See point 3 of this document. * In addition, in this case we have used environment variables, see point 4 of this document, so the values will be those established in that file. * <html><FONT COLOR="red">Comment 2:</FONT></html> * This line is used to set the time zone within the container. Adjust it to your location. If you leave the previous value ''Etc/UTC'' the container will use the Coordinated Universal Time. * It should appear in the OMV GUI in the SYSTEM > DATE & TIME tab and in the TIME ZONE field. If not, you can see it by typing ''cat /etc/timezone'' in a terminal. * In addition, in this case we have used environment variables, see point 4 of this document, so the values will be those established in that file. * <html><FONT COLOR="red">Comment 3:</FONT></html> * This is a special configuration of this container. To know what it means, it is necessary to consult the container documentation on the linuxserver website mentioned above. In some you may need this line to make Jellyfin visible on your network. We assume that our system does not need this so we add a hash at the beginning of that line so that docker ignores this instruction. If you need this you just have to remove ''#'' at the beginning and replace the IP with the real IP of your server. * If your IP were for example ''192.168.1.100'' the line would look like this: ''- JELLYFIN_PublishedServerUrl=192.168.1.100'' == Volumes == * <html><FONT COLOR="red">Comment 4:</FONT></html> * In the VOLUMES section we map folders from the container to the host and vice versa. **The user //appuser// must have read and write permissions on the ///appdata// folder and at least read permissions on the media folders. Otherwise, the container will throw an error when we start it.** * ...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> On the left the folder on the host. On the right the folder in the container.<br> Read the introduction of this document to understand folder mapping. </tr></table></body></html> * ...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#2C6700;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Advanced configuration </span></strong></td></tr><tr><td style="background-color:#e7fae1;height:25px;width:380px;"> You can use relative paths. Set the volume mapping in the compose file:<br> <b>- ./config:/config</b><br> will create the folder<br> <b>${PATH_TO_APPDATA}/jellyfin/config</b><br> on the host. </tr></table></body></html> * In the first line we are mapping the ///config// folder of the jellyfin container to a folder on our system. The ///config// folder is the one that contains the jellyfin configuration files, the database, users and passwords, plugins, etc. We want this folder to be located on a drive with access speed. So we map it to our ///appdata// folder that we have configured on a fast disk, ideal for managing a large database. A fast disk will allow Jellyfin to quickly read movie covers, etc. from the television and everything will be better. * ...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> This will be the persistent data for this container. If we need to reset the container, it is enough to stop it, delete this folder and start the container. We will find it in its initial state and ready to configure from scratch. </tr></table></body></html> * In the second line we map the folder containing our movies and photos so that the container can read them. * On the left the real path of this folder. In our case the actual path could be something like ''/srv/mergerfs/pool/data/media''. We can use that route or take advantage of the facilities that the plugin gives us to define routes. * In this case we have used the path defined in the plugin GUI (In SERVICES > COMPOSE > SETTINGS in the DATA Section) to make it easier to write paths. ''CHANGE_TO_COMPOSE_DATA_PATH'' is equivalent in our case to ''/srv/mergerfs/pool/data'' * An alternative way to do this would be to use the global environment variable that we added to the global environment variables file. ''PATH_TO_DATA=/srv/mergerfs/pool/data''. In the compose file we would write this line ''- ${PATH_TO_DATA}/media:/media'' * Next the separator '':'' * On the right, the name that we want the container to see, in this case we choose simply ''/media'' * ...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Note </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> In the case of jellyfin the libraries are configured from the container, we just need it to be able to see them. That is to say, it is not necessary to map movies on the one hand and photos on the other hand, although we could do it that way too.<br> To make it easier, we map a single volume that contains everything. Later from jellyfin we will search each folder for each library. /media/movies /media/photos etc. </tr></table></body></html> * If you have shared folders on other hard drives that you need the container to see, you can add as many volumes as you need in this section. == Devices == * <html><FONT COLOR="red">Comment 5:</span></FONT></html> * In the DEVICES section we can mount existing devices on the host inside the container. * This step is optional, set it only if you need [[https://jellyfin.org/docs/general/administration/hardware-acceleration/|Hardware Acceleration for Jellyfin]]. If you don't need it, simply remove these two lines from your compose file. * As an example in this case we will assume that we want to use Hardware Acceleration in Jellyfin and that the server processor is Intel and has an integrated GPU with [[https://en.wikipedia.org/wiki/Intel_Quick_Sync_Video|Intel Quick Sync]]. If we consult the linuxserver documentation it tells us that to use an Intel GPU we must mount the video device inside the container and we must also grant permission to the container user to access that device (see the linuxserver documentation to customize if your GPU is different). * To mount the video device we simply add the ''Devices:'' section to the compose file and mount the ''/dev/dri'' volume so that Jellyfin can read it using the line ''- /dev/dri:/dev/dri'' * When the container looks for the ''/dev/dri'' folder on his file system docker will cause the container to actually read the server filesystem folder ''/dev/dri'' * To grant //appuser// permissions to use this device we include it in the //render// and //video// groups. That will be enough to access the device and use it. == Ports == * <html><FONT COLOR="red">Comment 6:</span></FONT></html> * In the PORTS section we map the ports for the application to communicate with the outside. * The process and syntax are the same as in the other sections of the compose file. * If we want we can change the port that we will use in our system to access jellyfin or keep it the same. * In this case we keep it the same port, so we write ''8096'' on both sides of '':'' * If we wanted to change the port to 8888, for example, we would write on the left ''8888'', then the separator '':'' and on the right the one that specifies the original stack ''8096'', which is used internally by the container. The result would be ''- 8888:8096'' * If you need information about available ports you can check this forum post. [[https://forum.openmediavault.org/index.php?thread/28506-how-to-define-exposed-ports-in-docker-which-do-not-interfere-with-other-services/|[How-To] Define exposed ports in Docker which do not interfere with other services/applications]] * ...<html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> You should always make sure that the port mapped on the host is free.<br> There are special cases where the container needs port 80 and/or port 443, with Nginx Proxy Manager. OMV uses those ports to access the GUI. You can change them in System Workbench.<br> Another special case is pihole, which needs port 53. OMV uses port 53 and we cannot occupy it, so in this case it can be solved with a VLAN. There is a procedure in the openmediavault-compose plugin document on this wiki. </tr></table></body></html> When you run that container, what will actually be executed is the following. If you don't want to use global environment variables you can do it just like this. It will work the same way: <html><body><pre><code> services: jellyfin: image: lscr.io/linuxserver/jellyfin:latest container_name: jellyfin environment: - PUID=1002 - PGID=100 - TZ=Europe/Madrid volumes: - /srv/dev-disk-by-uuid-9d43cda9-20e5-474f-b38b-6b2b6c03211a/system/appdata/jellyfin/config:/config - /srv/mergerfs/pool/data/media:/media devices: - /dev/dri:/dev/dri ports: - 8096:8096 restart: unless-stopped </code></pre></body></html> \\ === Deploy the Container and access the application === {{ :omv7:dockeromv7-13.jpg?direct&1000 |Expand image -> Deploy the compose file}} * In the OMV GUI, go to SERVICES > COMPOSE > FILES. At this point the line corresponding to the compose file should appear with a red indicator on the right side saying that the container is stopped. Select the compose file and click on the UP button. * A code box will appear showing the output of this command in which you can see that the jellyfin image is being downloaded, the container is configured and its execution starts. Press CLOSE to close the code box. If everything went well you should see the line of that compose file with the indicator on the right in green. * If the container configuration is not correct, a red box will appear in the GUI indicating that there is an error. In that case you can debug it by doing the following: * Select the compose file and press the CHECK button. * A dialog box will open analyzing the composition of the container. You can probably figure out what the error is by reading that text. * Modify the compose file to correct the error and try again. * If the container configuration is correct, you should be able to access your application by typing the IP of our server followed by '':'' and the access port defined in the previous point. In this case, if the IP of our server were, for example, 192.168.1.100, we would write ''%%http://192.168.1.100:8096%%'' to access Jellyfin. ---- === Help request on the OMV forum === {{ :omv7:dockeromv7-20.jpg?direct&400|Expand image -> Forum help}} * If you have come this far and cannot lift your container, you can request help in the OMV forum. * **Whenever you ask for help on the forum related to running a container, post the compose file (and the global environment variables file if you are using it) inside a code box**. * To open a code box press the **''</>''** button in the toolbar of the box in which you write the post. * Copy and paste the code inside. * Hide sensitive data, such as passwords or email addresses or web domains. * Be grateful for help when you receive it, keep in mind that all forum members are volunteers, including OMV and omv-extras developers. ---- \\ {{ :divider2.png?nolink&800 |}} ===== Examples of configuration of some containers ===== \\ \\ Once the global environment variables file is configured, implementing containers is very simple. Some examples may be those listed below. In each of them the container has been configured following the system and folder scheme outlined in this document. Adapt it to your server configuration if it is different. \\ \\ ---- === Duplicati === Useful application to make encrypted, versioned, compressed and deduplicated backups locally or remotely. [[https://www.duplicati.com/|{{ :omv7:dockeromv7-14.png?200|Go to -> https://www.duplicati.com/}}]] <html><body><pre><code> # https://hub.docker.com/r/linuxserver/duplicati services: duplicati: image: lscr.io/linuxserver/duplicati:latest container_name: duplicati environment: - PUID=${APPUSER_PUID} - PGID=${APPUSER_PGID} - TZ=${TIME_ZONE_VALUE} #- CLI_ARGS= #optional volumes: - ${PATH_TO_APPDATA}/duplicati/config:/config - ${PATH_TO_BACKUPS}/duplicati/backups:/backups - ${PATH_TO_DOCUMENTS}:/source/documents:ro # :ro makes the container only have read access to this volume - ${PATH_TO_PHOTOS}:/source/photos:ro ports: - 8200:8200 restart: unless-stopped ########################### # This compose file is customized following the document: "Docker in OMV" from the OMV-Extras wiki. # Adapt it to your server if the configuration is different. # https://wiki.omv-extras.org/doku.php?id=omv7:docker_in_omv ########################### </code></pre></body></html> ---- === Syncthing === Application to synchronize folders between different devices and the server, such as smartphones or PCs. [[https://syncthing.net/|{{ :omv7:dockeromv7-15.png?200|Go to -> https://syncthing.net/}}]] <html><body><pre><code> # https://hub.docker.com/r/linuxserver/syncthing services: syncthing: image: lscr.io/linuxserver/syncthing:latest container_name: syncthing hostname: syncthing #optional environment: - PUID=${APPUSER_PUID} - PGID=${APPUSER_PGID} - TZ=${TIME_ZONE_VALUE} volumes: - ${PATH_TO_APPDATA}/syncthing/config:/config - ${PATH_TO_DOCUMENTS}/mary/syncthing:/mary # If your name is not Mary, modify this - ${PATH_TO_DOCUMENTS}/peter/syncthing:/peter # If your name is not Peter, modify this ports: - 8384:8384 - 22000:22000/tcp - 22000:22000/udp - 21027:21027/udp restart: unless-stopped ########################### # This compose file is customized following the document: "Docker in OMV" from the OMV-Extras wiki. # Adapt it to your server if the configuration is different. # https://wiki.omv-extras.org/doku.php?id=omv7:docker_in_omv ########################### </code></pre></body></html> ---- === Nginx Proxy Manager === This container will allow you to publish services (for example jellyfin) on the internet securely with Let's encrypt certificates. It has a very intuitive administration GUI. * As a special feature, notice that NPM needs to use ports 80 and 443 on the server. Since the OMV GUI is using them by default, you will have to release those ports so that the container can use them. * In the OMV GUI go to SYSTEM > WORKBENCH and change those two ports to others. For example, change 80 to 8888 and 443 to 8443. Or whatever you want that is not in use. * When you change them you will have to add the port to your browser's IP to access the OMV GUI. Example: %%http://192.168.1.50:8888%% <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#2C6700;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Advanced configuration. </span></strong></td></tr><tr><td style="background-color:#e7fae1;height:25px;width:380px;"> NPM requires ports 80 and 443 on the router to validate Let's Encrypt certificates. You can free those ports by changing the ones used by the OMV GUI as suggested or you can do a forwarding from the router to the container.<br> To do this, forward 80 and 443 on the router to, for example, 30080 and 30443 with the server's IP. In the NPM container it collects these ports using:<br> - 30080:80<br> - 30443:443<br> The result will be the same, the container will receive the traffic from those router ports and you will still have the OMV GUI on port 80 and 443 of your local network. </tr></table></body></html> [[https://nginxproxymanager.com/|{{ :omv7:dockeromv7-21.png?200|Go to -> https://nginxproxymanager.com/}}]] <html><body><pre><code> # https://nginxproxymanager.com services: app: image: 'jc21/nginx-proxy-manager:latest' restart: unless-stopped ports: - '80:80' # Public HTTP Port - '443:443' # Public HTTPS Port - '81:81' # Admin Web Port # Add any other Stream port you want to expose # - '21:21' # FTP environment: # Mysql/Maria connection parameters: DB_MYSQL_HOST: "db" DB_MYSQL_PORT: 3306 DB_MYSQL_USER: "npm" DB_MYSQL_PASSWORD: "npm" DB_MYSQL_NAME: "npm" # Uncomment this if IPv6 is not enabled on your host # DISABLE_IPV6: 'true' volumes: - ${PATH_TO_APPDATA}/nginxproxymanager/data:/data - ${PATH_TO_APPDATA}/nginxproxymanager/letsencrypt:/etc/letsencrypt depends_on: - db db: image: 'jc21/mariadb-aria:latest' restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: 'npm' MYSQL_DATABASE: 'npm' MYSQL_USER: 'npm' MYSQL_PASSWORD: 'npm' MARIADB_AUTO_UPGRADE: '1' volumes: - ${PATH_TO_APPDATA}/nginxproxymanager/mysql:/var/lib/mysql ########################### # This compose file is customized following the document: "Docker in OMV" from the OMV-Extras wiki. # Adapt it to your server if the configuration is different. # https://wiki.omv-extras.org/doku.php?id=omv7:docker_in_omv ########################### </code></pre></body></html> \\ To access the NPM GUI use port 81. The initial username and password to access NPM is: * User: ''admin@example.com'' * Password: ''changeme'' ---- === Nextcloud AIO (All In One) === Nextcloud is a private cloud hosting system. You will be able to access the files on your server from the internet. This container is the official version of Nextcloud AIO (All In One). It has a configuration GUI that installs and manages several containers at the same time. * The official Nextcloud AIO docker container documentation is here -> [[https://github.com/nextcloud/all-in-one?tab=readme-ov-file#nextcloud-all-in-one|Nextcloud AIO]] * The official Nextcloud administration documentation is here -> [[https://docs.nextcloud.com/server/latest/admin_manual/|Nextcloud]] Before installing Nextcloud you need to previously install a proxy, such as the one described above Nginx Proxy Manager (you can use any other proxy supported by Nextcloud AIO, check their documentation for other options). Install NPM first and configure it following these instructions: [[https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md#nginx-proxy-manager|NPM configuration for Nextcloud AIO]] (Click on "click to expand" in the option for Nginx-Proxy-Manager) For this container to work you will have to do the following: * You will need a domain, you can buy it or get a free one, for example at [[https://www.duckdns.org/|Duckdns.org]] * Point the domain to your router's public IP (make sure your ISP hasn't put you behind [[https://en.wikipedia.org/wiki/Carrier-grade_NAT|CGNAT]]. If this is the case, request a public IP without CGNAT). Use this site to check if it's working: [[https://www.whatsmydns.net/|www.whatsmydns.net]] The IP must be the same as the one configured on your router. You can press several times to force the expansion. * On your router, direct ports 80 and 443 to the IP of your server. The proxy (Nginx Proxy Manager) will receive the traffic from these ports and will direct it to the Nextcloud container through port 11000 (or to another container, if the request reaches the proxy from a domain other than Nextcloud). [[https://github.com/nextcloud/all-in-one?tab=readme-ov-file#nextcloud-all-in-one|{{ :omv7:dockeromv7-22.jpg?200|Go to -> https://github.com/nextcloud/all-in-one?tab=readme-ov-file#nextcloud-all-in-one}}]] <html><body><pre><code> # https://github.com/nextcloud/all-in-one # For custom configuration consult -> https://github.com/nextcloud/all-in-one/blob/main/compose.yaml services: nextcloud-aio-mastercontainer: image: nextcloud/all-in-one:latest init: true restart: always container_name: nextcloud-aio-mastercontainer volumes: - nextcloud_aio_mastercontainer:/mnt/docker-aio-config - /var/run/docker.sock:/var/run/docker.sock:ro ports: - 8080:8080 environment: - APACHE_PORT=11000 - NEXTCLOUD_DATADIR=${PATH_TO_APPDATA}/nextcloud_data volumes: nextcloud_aio_mastercontainer: name: nextcloud_aio_mastercontainer ########################### # This compose file is customized following the document: "Docker in OMV" from the OMV-Extras wiki. # Adapt it to your server if the configuration is different. # https://wiki.omv-extras.org/doku.php?id=omv7:docker_in_omv ########################### </code></pre></body></html> \\ Start the container and make the first configuration. Follow these steps from point 4 -> [[https://nextcloud.com/blog/how-to-install-the-nextcloud-all-in-one-on-linux/|How to install the Nextcloud All In One on linux]] Notice that we have configured the Nextcloud data volume in the //appdata// folder. This is due to two reasons. * All files that Nextcloud manages directly are synchronized in a database and Nextcloud can modify their permissions. This may prevent you from using those files from outside of Nextcloud due to permissions and would in any case affect the Nextcloud database. * Nextcloud AIO provides a backup system in its GUI that specifically includes all user data. This may not be convenient if those folders are too large and you use other means to back up that data. All of this can be easily overcome by using the [[https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/external_storage_configuration_gui.html|Nextcloud External Storage Plugin]] to mount those shared folders from the Nextcloud GUI, for example with samba. This will allow you to continue using those files within Nextcloud but outside of its database. Then in the data volume set up for Nextcloud there will only be information such as phone books or calendars of the different users, which do not take up much space and can be stored on a small and fast disk. <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Note </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> Nextcloud AIO is a container that spawns other containers and stores them in the docker folder. The openmediavault-compose plugin backup utility does not back up data in this folder.<br> If you want to have a backup of this container you must use the internal backup function of Nextcloud AIO. In the Nextcloud AIO GUI you must establish where this backup is saved and how often it should be done, Nextcloud AIO takes care of everything else, stopping containers, making the backup and starting containers again. </tr></table></body></html> ---- === Other containers === {{ :omv7:dockeromv7-23.jpg?400 |Expand image -> Add from example}} * Look at the examples provided by the compose plugin, there are many prepared compose files. * In the OMV GUI go to SERVICES > COMPOSE > FILES and press the ADD button, then press the ADD FROM EXAMPLE button. <html><body><table width="100%" border="0"><tr><td colspan="2" style="background-color:#69A5FF;height:30px;"><strong><span style="color:#FFFFFF;font-size:110%;">  Beginners Info </span></strong></td></tr><tr><td style="background-color:#E6FEFF;height:25px;width:380px;"> Most of the example compose files will work out of the box if you run them without making any modifications. But it will probably be better to adapt them to your system configuration according to everything explained in this document. This will avoid unexpected situations. </tr></table></body></html> ---- === Create your own custom container === \\ [[omv7:omv7_plugins:docker_compose#dockerfiles|{{ :omv7:dockeromv7-27.jpg?200|Go to -> Dockerfiles}}]] If you can't find a container that fits what you need in the plugin's list of examples or on the internet, you can create an image yourself and run the container from that image. To do this you can use Dockerfile. The openmediavault-compose plugin makes it easy to create images using the Dockerfile. You can see its use here -> [[omv7:omv7_plugins:docker_compose#dockerfiles|Dockerfiles]] ---- \\ {{ :divider2.png?nolink&800 |}} ===== Some basic procedures for container management ===== \\ \\ === How to schedule container updates and/or backups === [[omv7:omv7_plugins:docker_compose#schedule_updates_and_backups|{{ :omv7:dockeromv7-25.jpg?200|Go to -> Schedule (Updates and Backups)}}]] Especially useful is this feature of the plugin. You will be able to selectively schedule container updates. And you can also make backups of the containers and volumes you want on a scheduled basis. See how to do it in the corresponding section of the plugin document in this wiki -> [[omv7:omv7_plugins:docker_compose#schedule_updates_and_backups|Schedule (Updates and Backups)]] ---- === How to modify the configuration of a container === If for any reason you need to modify the container configuration, change the location of a volume or any other circumstance, do the following: * In the OMV GUI go to SERVICES > COMPOSE > FILES, select the container row and press the DOWN button. This will stop the container. * Press the EDIT button. * Modify the desired parameters in the FILE box. * Press SAVE button. * Select the container row again and press the UP button. The container will now be running with the modified parameters. ---- === How to reset a container's settings === If you want to restore the container to its initial state, do the following (This will remove any configuration we have made to the container): * In the OMV GUI go to SERVICES > COMPOSE > FILES, select the container row and press the DOWN button. This will stop the container. * Delete the config folder corresponding to the container. In the example ''/srv/dev-disk-by-uuid-9d43cda9-20e5-474f-b38b-6b2b6c03211a/appdata/jellyfin/config'' * This folder contains all the configurations that we have made inside the container. * When the container starts again it will recreate the files in this folder, so no configuration will exist. We can start configuring it from scratch again. * In the OMV GUI go to SERVICES > COMPOSE > FILES, select the container row and press the UP button. This will start the container. ---- === Other procedures === [[omv7:omv7_plugins:docker_compose#usual_procedures|{{ :omv7:dockeromv7-26.jpg?200|Go to -> Usual procedures}}]] \\ You can find several useful procedures in the corresponding section of the openmediavault-compose plugin document. Among them you will find a procedure to configure a vlan that will allow you to install **pihole** or **adguard**. -> [[omv7:omv7_plugins:docker_compose#usual_procedures|Usual procedures]] ---- \\ {{ :divider2.png?nolink&800 |}} ===== The whys ===== \\ \\ === Why use global environment variables === [[omv7:docker_in_omv#global_environmental_variables|{{ :omv7:dockeromv7-24.jpg?200|Go to -> Global environment variables}}]] * If you change a path or any other variable that affects multiple containers, it is enough to vary this value in the global environment variables file. The value will change automatically in all containers. Useful if you change a data drive, or if you reinstall OMV and change routes, for example. * It is integrated into the plugin, it is enough to press a button to access the file to directly introduce the variables without doing anything else. * They allow us to define a value once and all the containers that we create will use this value automatically. * Useful to define routes with UUID, it is only necessary to enter it once and we will have it available to use in each container. * Useful to reuse containers if we perform a reinstallation of OMV. We just need to update the values of these variables and the containers will continue to work just like before. * Avoid using symbolic links if you don't need them for another reason. Despite that you could still combine the two systems if you need to. ---- === Why use symlinks === [[omv7:omv7_plugins:symlinks|{{ :omv7:omv7_plugins:symlinks-logo.jpg?200|Go to -> Symlinks Plugin For OMV7}}]] * It is integrated into OMV thanks to the [[omv7:omv7_plugins:symlinks|openmediavault-symlinks]] plugin * When working with container configuration yaml files, we will likely need to copy the mount paths from our file system and they will likely be paths with UUIDs. These routes are very long and difficult to manage. * A symlink is a shortcut to the folder we need. This is useful to avoid copying and pasting long flowing paths. * Makes it easy to quickly change a folder path across all containers without modifying them one by one. Simply modify a symbolic link and all containers will be up to date. * The use of symbolic links is not mandatory, the goal is ease of use. If you prefer to use full paths, you can skip this point. You can alternatively use the global environment variables file provided by the plugin or combine both. ---- === Why use docker-compose === [[https://docs.docker.com/compose/|{{ :omv7:dockeromv7-3.png?direct&200|Go to -> https://docs.docker.com/compose/}}]] * It is integrated into OMV thanks to the openmediavault-compose plugin * It is easy to implement, manage, edit, configure... * Most docker container publishers offer a ready stack for docker-compose in their documentation. * Sometimes a container publisher does not provide a stack for docker-compose. In that case you can generate it yourself with a little experience. There are also tools to do it automatically online like this one. [[https://www.composerize.com/|www.composerize.com]] * If you already have a working container you can generate the stack automatically from the openmediavault-compose plugin with [[omv7:omv7_plugins:docker_compose#autocompose/|autocompose]] ---- === Why use openmediavault-compose === [[omv7:omv7_plugins:docker_compose|{{ :omv7:omv7_plugins:compose-logo.jpg?direct&200|Go to -> (Docker) Compose Plugin For OMV7}}]] * openmediavault-compose * It covers all the needs of the vast majority of OMV users to use docker. * It is integrated into the OMV GUI and into the system. * It is automatically updated along with all other system updates. * Makes it easy to manage and store container composition files. * Facilitates the automatic updating and backup of containers on a scheduled and selective basis. * openmediavault plugins are under constant development and new features and fixes are added as the need arises. * It has the support of the OMV forum to solve any problem. * Portainer * It is not integrated into the GUI, you have to access it through another tab and port of your browser. * It is one more container, it needs manual updates periodically. * It does not store the composition files, you have to do it yourself. * It doesn't update containers, you have to do it manually. * If you have a functional problem you will have to request assistance from the Portainer developers. * Disables container management from openmediavault-compose. ---- === Why use 64 bits? === [[omv7:docker_in_omv#what_is_docker|{{ :omv7:dockeromv7-17.jpg?direct&200|Go to -> What is docker}}]] * You should use a 64-bit system if your hardware allows it, 32-bit systems have been out of use for many years now. The compatibility of many software packages is still maintained, but as time goes by they are disappearing. * Docker starts to be inaccessible for 32-bit systems. Many reputable container builders are stopping releasing 32-bit versions. * OMV can also cause problems on 32-bit systems with docker. ---- \\ {{ :divider2.png?nolink&800 |}} ===== A Closing Note ===== We, who support the openmediavault project, hope that you’ll find your openmediavault server to be enjoyable, efficient, and easy to use.\\ \\ If you found this guide to be helpful, please consider a modest donation to support the hosting costs of this server (OMV-Extras) and the project (Openmediavault). \\ \\ **OMV-Extras.org** \\ <html> <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top"> <input type="hidden" name="cmd" value="_s-xclick"> <input type="hidden" name="hosted_button_id" value="2BQNGSC8HQJME"> <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Donate with PayPal button" border="0"> <img alt="" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" border="0"> </form> </html> \\ \\ **www.openmediavault.org** \\ <html> <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank"> <input name="cmd" type="hidden" value="_s-xclick" /> <input name="hosted_button_id" type="hidden" value="95MF5UQ66PW2E" /> <input style="border: 0px; padding: 0px;" alt="PayPal - The safer, easier way to pay online!" name="submit" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" type="image" /> <img loading="lazy" src="https://www.paypalobjects.com/de_DE/i/scr/pixel.gif" alt="" width="1" height="1" border="0" /> </form> </html> \\ \\ \\ docs_in_draft/docker_in_omv.txt Last modified: 2025/12/08 18:01by chente Currently locked by: chente