diff --git a/Dockerfile b/Dockerfile index 56f355d..45f824d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,9 @@ RUN echo user_allow_other >> /etc/fuse.conf COPY entrypoint.sh entrypoint.sh +RUN mkdir /config +COPY parameters.conf /config/parameters.conf + RUN mkdir /disks && \ chmod +x entrypoint.sh diff --git a/README.md b/README.md index 647d4b2..86900e2 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,63 @@ services: ``` ## Customizing -* If you would like to customize the mergerfs command with additional options, you can overwrite entrypoint.sh by mounting your own using an additional volume mount. -* If you would like to combine this with samba to share it over the network, you could also use a samba docker container. +* If you would like to customize the mergerfs command with additional options, you can overwrite `parameters.conf` with your own using an additional volume mount that maps onto `/config` inside the docker. You can use the one in the repo as a template. +* You can also override the default `mergerfs` parameters by using the `MERGERFS_PARAMS` environment variable as follows: +```yaml +services: + mergerfs: + image: hvalev/mergerfs:latest + container_name: mergerfs + environment: + MERGERFS_PARAMS: 'moveonenospc=true,dropcacheonclose=true,category.create=mfs,cache.files=partial' + cap_add: + - SYS_ADMIN + devices: + - /dev/fuse:/dev/fuse + volumes: + - /mnt/nd1:/disks/nd1 + - /mnt/nd2:/disks/nd2 + - /mnt/nd3:/disks/nd3 + - /mnt/merged:/merged:shared + restart: always +``` +* If you would like to combine this with samba to share it over the network, you could also use a samba docker container. Below is an example with a sample user read-write and guest read-only access. +```yaml +services: + mergerfs: + image: hvalev/mergerfs:latest + container_name: mergerfs + environment: + MERGERFS_PARAMS: 'moveonenospc=true,dropcacheonclose=true,category.create=mfs,cache.files=partial' + cap_add: + - SYS_ADMIN + devices: + - /dev/fuse:/dev/fuse + volumes: + - /mnt/hd1:/disks/hd1 + - /mnt/hd2:/disks/hd2 + - /mnt/hd3:/disks/hd3 + - /mnt/hd:/merged:shared + restart: always + + samba: + image: elswork/samba:3.2.8 + container_name: samba + environment: + TZ: 'Europe/Amsterdam' + ports: + - 139:139 + - 445:445 + volumes: + - /mnt/hd:/mnt/hd + command: '-u "1000:1000:user:user:user_password" + -u "1000:1000:guest:guest:guest_password" + -s "hd:/mnt/hd:rw:user" + -s "media:/mnt/hd:ro:guest"' + restart: unless-stopped + depends_on: + - mergerfs +``` ## Acknowledgements The following resources have been extremely helpful: diff --git a/entrypoint.sh b/entrypoint.sh old mode 100644 new mode 100755 index 62adb5c..b523ebd --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,16 +1,63 @@ #!/bin/sh -function cleanup { +# Function to clean up on script exit +cleanup() { + echo "Unmounting mergerfs..." umount /merged + echo "Unmounted mergerfs." } -mergerfs -o allow_other,use_ino,cache.files=partial,dropcacheonclose=true,moveonenospc=true,category.create=mfs /disks/*: /merged +# Set debugging options +# set -e # Exit immediately if a command exits with a non-zero status +# set -x # Print commands and their arguments as they are executed +# Define the default parameters file +PARAMETERS_FILE="/config/parameters.conf" +log() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" +} + +# Determine source of parameters +if [ -n "$MERGERFS_PARAMS" ]; then + log "Using parameters from MERGERFS_PARAMS environment variable." + PARAMS="$MERGERFS_PARAMS" +else + if [ ! -f "$PARAMETERS_FILE" ]; then + log "Error: /config/parameters.conf not found and MERGERFS_PARAMS is not set." + exit 1 + fi + + # Merge parameters from the file into a single line without comments or empty lines + PARAMS=$(grep -v '^\s*#' "$PARAMETERS_FILE" | sed '/^\s*$/d' | sed 's/^[ \t]*//;s/[ \t]*$//' | paste -sd ',' -) + + if [ -z "$PARAMS" ]; then + log "Error: No valid parameters found in $PARAMETERS_FILE." + exit 1 + else + log "Collecting parameters from configuration file found at /config/parameters.conf" + fi +fi + + +# Prepare the mergerfs command +COMMAND="mergerfs -o $PARAMS /disks/*: /merged" + +# Print the command being executed +echo "Executing: $COMMAND" + +# Mount using mergerfs +$COMMAND + +# Trap SIGTERM signal of docker daemon +trap cleanup SIGTERM + +# Trap exit signals to ensure cleanup trap cleanup EXIT INT +# Wait for mergerfs process to finish while [[ $(ps -eo 'comm' | grep mergerfs -c) -gt 0 ]]; do sleep 1 done -echo mergerfs terminated \ No newline at end of file +echo "mergerfs terminated" diff --git a/parameters.conf b/parameters.conf new file mode 100644 index 0000000..68321da --- /dev/null +++ b/parameters.conf @@ -0,0 +1,239 @@ +# moveonenospc: When enabled, if a write fails with ENOSPC (no space left on device) or EDQUOT (disk quota exceeded), +# the policy selected will run to find a new location for the file. +# An attempt to move the file to that branch will occur (keeping all metadata possible) and if successful, +# the original is unlinked and the write retried. +# Default: false +moveonenospc=true + +# dropcacheonclose: When a file is requested to be closed, call posix_fadvise on it first to instruct the kernel +# that we no longer need the data and it can drop its cache. +# Recommended when cache.files=partial|full|auto-full|per-process to limit double caching. +# Default: false +dropcacheonclose=true + +# category.create: Sets policy of all FUSE functions in the create category. +# Default: epmfs - Uses "most free space" policy for creating files +# Available values: epmfs, eplfs, ff, lfs, newest, mfs, rand, all +category.create=mfs + +# cache.files: File page caching mode. +# Default: libfuse - Enables partial file caching +# Available values: libfuse, off, partial, full, auto-full, per-process +cache.files=partial + +# lazy-umount-mountpoint: mergerfs will attempt to "lazy umount" the mountpoint before mounting itself. +# Useful when performing live upgrades of mergerfs. +# Default: false +# lazy-umount-mountpoint=true + +# minfreespace: Minimum space value used for creation policies. +# Default: 4G +# Available values: any size (e.g., 1K, 10M, 5G) +# minfreespace=4G + +# inodecalc: Selects the inode calculation algorithm. +# Default: hybrid-hash +# Available values: passthrough, path-hash, devino-hash, hybrid-hash +# inodecalc=hybrid-hash + +# symlinkify: When enabled and a file is not writable and its mtime or ctime is older than symlinkify_timeout, +# files will be reported as symlinks to the original files. +# Default: false +# Available values: true, false +# symlinkify=false + +# nullrw: Turns reads and writes into no-ops. Useful for benchmarking mergerfs. +# Default: false +# Available values: true, false +# nullrw=false + +# ignorepponrename: Ignore path preserving on rename. +# Typically rename and link act differently depending on the policy of create. +# Default: false +# Available values: true, false +# ignorepponrename=false + +# export-support: Sets a low-level FUSE feature intended to indicate the filesystem can support being exported via NFS. +# Default: true +# Available values: true, false +# export-support=true + +# security_capability: If false, return ENOATTR when xattr security.capability is queried. +# Default: true +# Available values: true, false +# security_capability=true + +# xattr: Runtime control of xattrs. +# Default: passthrough +# Available values: passthrough, noattr, nosys +# xattr=passthrough + +# link_cow: When enabled, if a regular file is opened which has a link count > 1, +# it will copy the file to a temporary file and rename over the original. +# Breaking the link and providing a basic copy-on-write function similar to cow-shell. +# Default: false +# Available values: true, false +# link_cow=false + +# statfs: Controls how statfs works. +# Default: base +# Available values: base, full +# statfs=base + +# statfs_ignore: Controls how statfs calculations ignore certain branches. +# Default: none +# Available values: none, ro, nc +# statfs_ignore=none + +# nfsopenhack: A workaround for exporting mergerfs over NFS. +# Default: off +# Available values: off, git, all +# nfsopenhack=off + +# branches-mount-timeout: Number of seconds to wait at startup for branches to be a mount other than the mountpoint's filesystem. +# Default: 0 +# Available values: any integer value +# branches-mount-timeout=0 + +# follow-symlinks: Turns symlinks into what they point to. +# Default: never +# Available values: never, directory, regular, all +# follow-symlinks=never + +# link-exdev: When a link fails with EXDEV, optionally create a symlink to the file instead. +# Default: passthrough +# Available values: passthrough, rel-symlink, abs-base-symlink, abs-pool-symlink +# link-exdev=passthrough + +# rename-exdev: When a rename fails with EXDEV, optionally move the file to a special directory and symlink to it. +# Default: passthrough +# Available values: passthrough, rel-symlink, abs-symlink +# rename-exdev=passthrough + +# readahead: Set readahead (in kilobytes) for mergerfs and branches if greater than 0. +# Default: 0 +# Available values: any integer value +# readahead=0 + +# posix_acl: Enable POSIX ACL support (if supported by kernel and underlying filesystem). +# Default: false +# Available values: true, false +# posix_acl=false + +# async_read: Perform reads asynchronously. +# Default: true +# Available values: true, false +# async_read=true + +# fuse_msg_size: Set the max number of pages per FUSE message. +# Default: 256 +# Available values: 1 to 256 +# fuse_msg_size=256 + +# threads: Number of threads to use. +# When used alone (process-thread-count=-1), it sets the number of threads reading and processing FUSE messages. +# When used together, it sets the number of threads reading from FUSE. +# Default: 0 +# Available values: any integer value +# threads=0 + +# process-thread-count: Enables a separate thread pool to asynchronously process FUSE requests. +# In this mode, read-thread-count refers to the number of threads reading FUSE messages which are dispatched to process threads. +# Default: -1 +# Available values: -1 to any integer value +# process-thread-count=-1 + +# process-thread-queue-depth: Sets the number of requests any single process thread can have queued up at one time. +# Default: 0 +# Available values: any integer value +# process-thread-queue-depth=0 + +# pin-threads: Selects a strategy to pin threads to CPUs. +# Default: unset +# Available values: any valid strategy string +# pin-threads= + +# flush-on-close: Flush data cache on file close. +# Mostly for when writeback is enabled or merging network filesystems. +# Default: opened-for-write +# Available values: never, always, opened-for-write +# flush-on-close=opened-for-write + +# scheduling-priority: Set mergerfs' scheduling priority. +# Valid values range from -20 to 19. See setpriority man page for more details. +# Default: -10 +# Available values: -20 to 19 +# scheduling-priority=-10 + +# fsname: Sets the name of the filesystem as seen in mount, df, etc. +# Default: dynamically generated +# Available values: any string +# fsname=mergerfs + +# category.action: Sets policy of all FUSE functions in the action category. +# Default: epall +# Available values: epall, all +# category.action=epall + +# category.search: Sets policy of all FUSE functions in the search category. +# Default: ff +# Available values: ff, lfs, newest, rand, all +# category.search=ff + +# cache.open: 'open' policy cache timeout in seconds. +# Default: 0 +# Available values: any integer value +# cache.open=0 + +# cache.statfs: 'statfs' cache timeout in seconds. +# Default: 0 +# Available values: any integer value +# cache.statfs=0 + +# cache.attr: File attribute cache timeout in seconds. +# Default: 1 +# Available values: any integer value +# cache.attr=1 + +# cache.entry: File name lookup cache timeout in seconds. +# Default: 1 +# Available values: any integer value +# cache.entry=1 + +# cache.negative_entry: Negative file name lookup cache timeout in seconds. +# Default: 0 +# Available values: any integer value +# cache.negative_entry=0 + +# cache.writeback: Enable kernel writeback caching. +# Default: false +# Available values: true, false +# cache.writeback=false + +# cache.symlinks: Cache symlinks (if supported by kernel). +# Default: false +# Available values: true, false +# cache.symlinks=false + +# cache.readdir: Cache readdir (if supported by kernel). +# Default: false +# Available values: true, false +# cache.readdir=false + +# parallel-direct-writes: Allow the kernel to dispatch multiple, parallel (non-extending) write requests for files +# opened with cache.files=per-process (if the process is not in process-names) or cache.files=off. +# Default: false +# Available values: true, false +# parallel-direct-writes=false + +# Deprecated options (for reference only) +# direct_io=false # Default: false - Bypass page cache. Use cache.files=off instead. +# kernel_cache=false # Default: false - Do not invalidate data cache on file open. Use cache.files=full instead. +# auto_cache=false # Default: false - Invalidate data cache if file mtime or size change. Use cache.files=auto-full instead. +# async_read=true # Default: true - Perform reads asynchronously. Use async_read=true instead. +# sync_read=false # Default: false - Perform reads synchronously. Use async_read=false instead. +# splice_read=false # Default: false - Does nothing. +# splice_write=false # Default: false - Does nothing. +# splice_move=false # Default: false - Does nothing. +# allow_other=true # Default: true if running as root - mergerfs v2.35.0 and newer sets this FUSE option automatically if running as root. +# use_ino=true # Default: true - mergerfs should always control inode calculation so this is enabled all the time.