diff --git a/.drone.yml b/.drone.yml
index 0c804e0..8999d06 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -5,7 +5,7 @@ name: default
 
 steps:
   - name: build:binary
-    image: crystallang/crystal:1.7.3
+    image: crystallang/crystal:1.10.1-alpine
     environment:
       PACKAGE_BASENAME: mfm_linux_amd64
     volumes:
@@ -13,19 +13,27 @@ steps:
         path: /_cache
     commands:
       - pwd
-      - apt-get update && 
-          apt-get install -y cmake g++ libevent-dev libpcre3-dev libyaml-dev
+        # - |
+        #   apt-get update && \
+        #   apt-get install -y \
+        #     cmake g++ \
+        #     libevent-dev libpcre3-dev \
+        #     libyaml-dev liblzma-dev
       - shards install
       - shards build --production --static
       - strip bin/mfm
+      - ./bin/mfm --version
       - mkdir -p /_cache/bin
       - cp -r bin/mfm /_cache/bin/$PACKAGE_BASENAME
 
   - name: publish:tag
-    image: curlimages/curl
+    image: alpine
     environment:
-      PACKAGE_UPLOAD_URL: https://code.apps.glenux.net/api/packages/glenux/generic/mfm
+      PACKAGE_UPLOAD_URL: https://code.apps.glenux.net/api/v1/packages/glenux/generic/mfm
+      RELEASES_URL: https://code.apps.glenux.net/api/v1/repos/glenux/mfm/releases
       PACKAGE_BASENAME: mfm_linux_amd64
+      RELEASE_UPLOAD_TOKEN:
+        from_secret: RELEASE_UPLOAD_TOKEN
       PACKAGE_UPLOAD_TOKEN:
         from_secret: PACKAGE_UPLOAD_TOKEN
     when:
@@ -36,16 +44,52 @@ steps:
       - name: cache
         path: /_cache
     commands:
+      - apk add --update --no-cache curl jq
       - env |grep DRONE
       - |
         curl -H "Authorization: token $PACKAGE_UPLOAD_TOKEN" \
-             --upload-file /_cache/bin/$PACKAGE_BASENAME \
-           $PACKAGE_UPLOAD_URL/$DRONE_TAG/$PACKAGE_BASENAME
+           --upload-file "/_cache/bin/$PACKAGE_BASENAME" \
+           "$PACKAGE_UPLOAD_URL/$DRONE_TAG/$PACKAGE_BASENAME"
+      - |
+        set -x
+        curl -X POST \
+          -H "Authorization: token $RELEASE_UPLOAD_TOKEN" \
+          -H 'accept: application/json' \
+          -H 'Content-Type: application/json' \
+          -d "{\"body\": \"DRAFT\", \"draft\": true, \"name\": \"$DRONE_TAG - DRAFT\", \"prerelease\": false, \"tag_name\": \"$DRONE_TAG\",  \"target_commitish\": \"$DRONE_COMMIT_SHA\"}" \
+          "$RELEASES_URL"
+      - |
+        curl -X 'GET' \
+          -H 'accept: application/json' \
+          "$RELEASES_URL/tags/$DRONE_TAG"
+      - |
+        TAG_ID="$(curl -X 'GET' \
+          -H 'accept: application/json' \
+          "$RELEASES_URL/tags/$DRONE_TAG" | jq -r .id)"
+        echo "TAG_ID=$TAG_ID"
+      - |
+        set -x
+        curl -X POST \
+           -H "Authorization: token $RELEASE_UPLOAD_TOKEN" \
+           -H "accept: application/json" \
+           -H "Content-Type: multipart/form-data" \
+           -F "attachment=@/_cache/bin/$PACKAGE_BASENAME" \
+           "$RELEASES_URL/$TAG_ID/assets?name=$PACKAGE_BASENAME"
+
 
 # FIXME: handle multi-arch
 # FIXME: publish only on tags
+services:
+  - name: docker
+    image: docker:dind
+    privileged: true
+    volumes:
+      - name: dockersock
+        path: /var/run
 
 volumes:
   - name: cache
     temp: {}
+  - name: dockersock
+    temp: {}
 #
diff --git a/README.md b/README.md
index 15dc95a..bc58143 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,8 @@
 # Copyright © 2023 Glenn Y. Rolland <glenux@glenux.net>
 -->
 
+[![Build Status](https://cicd.apps.glenux.net/api/badges/glenux/mfm/status.svg)](https://cicd.apps.glenux.net/glenux/mfm)
+
 # Minimalist Fuse Manager (MFM)
 
 MFM is a Crystal-lang CLI designed to streamline the management of various FUSE filesystems, such as sshfs, gocryptfs, httpdirfs, and more. Through its user-friendly interface, users can effortlessly mount and unmount filesystems, get real-time filesystem status, and handle errors proficiently.
@@ -17,11 +19,27 @@ Before using MFM, make sure the following tools are installed on your system:
 - **sshfs**: <https://github.com/libfuse/sshfs>
 - **httpdirfs**: <https://github.com/fangfufu/httpdirfs>
 - **fzf**: <https://github.com/junegunn/fzf>
+- libpcre3
+- libevent-2.1
+
+For Debian/Ubuntu you can use the following command:
+
+```shell-session
+$ sudo apt-get update && sudo apt-get install libpcre3 libevent-2.1-7 fzf gocryptfs httpdirfs sshfs
+```
+
+## Building from source
 
 To build from source, you'll also need:
 
 - **crystal-lang**: <https://crystal-lang.org/>
 
+For Debian/Ubuntu you can use the following command:
+
+```shell-session
+$ sudo apt-get update && sudo apt-get install libpcre3-dev libevent-2.1-dev
+```
+
 ## Installation
 
 ### 1. From Source
@@ -34,7 +52,8 @@ To build from source, you'll also need:
 
 ### 2. Binary Download
 
-Alternatively, download a pre-compiled binary version of MFM.
+Alternatively, download [a pre-compiled binary
+version](https://code.apps.glenux.net/glenux/mfm/releases) of MFM.
 
 ## Usage
 
@@ -47,7 +66,7 @@ Global options:
     -c, --config FILE                Specify configuration file
     -h, --help                       Display this help
 
-Commands:
+Commands (not implemented yet):
     create                           Add a new filesystem
     delete                           Remove an existing filesystem
     edit                             Modify the configuration
@@ -59,7 +78,8 @@ Commands:
 
 ## Configuration
 
-MFM uses a YAML configuration file, typically found at `~/.config/mfm.yml`, to detail the filesystem names, types, and respective configurations.
+MFM uses a YAML configuration file, typically found at `~/.config/mfm.yml`, to
+detail the filesystem names, types, and respective configurations.
 
 ### YAML File Format
 
@@ -84,7 +104,7 @@ filesystems:
   - type: httpdirfs
     name: "Debian Repository"
     url: "http://ftp.debian.org/debian/"
-  
+
   # Add more filesystems as needed
 ```
 
@@ -100,7 +120,7 @@ Contributing to MFM:
 6. **Submit a Pull Request**: Begin a pull request to the main repository and explain your changes.
 7. **Review**: Await feedback from the maintainers and respond as necessary.
 
-By contributing, you agree to our code of conduct and GPL-2 license terms.
+By contributing, you agree to our code of conduct and license terms.
 
 ## Authors and Contributors
 
diff --git a/Vagrantfile b/Vagrantfile
index d5596e1..6ea27b7 100644
--- a/Vagrantfile
+++ b/Vagrantfile
@@ -30,5 +30,5 @@ Vagrant.configure('2') do |config|
     machine.vm.network 'forwarded_port', guest: 80, host: 1080, host_ip: '127.0.0.1'
   end
 
-  config.vm.provision 'shell', path: 'scripts/vagrant.provision.sh'
+  config.vm.provision 'shell', path: 'scripts/vagrant-provision/base.sh'
 end
diff --git a/scripts/ci.crossbuild-alpine.sh b/scripts/ci.crossbuild-alpine.sh
new file mode 100755
index 0000000..9a870e6
--- /dev/null
+++ b/scripts/ci.crossbuild-alpine.sh
@@ -0,0 +1,64 @@
+#!/bin/sh -eu
+# vim: set ts=2 sw=2 et:
+
+LOCAL_PROJECT_PATH="${1-$PWD}"
+
+TARGET_ARCH="${2-amd64}"
+
+DOCKER_IMAGE=""
+
+BUILD_COMMAND=" \
+  shards build --static --release \
+  && chown 1000:1000 -R bin \
+  && find bin -type f -maxdepth 1 -exec mv {} {}_${TARGET_ARCH} \; \
+"
+INSTALL_CRYSTAL=" \
+  echo '@edge http://dl-cdn.alpinelinux.org/alpine/edge/community' >>/etc/apk/repositories \
+  && apk add --update --no-cache --force-overwrite \
+    crystal@edge \
+    g++ \
+    gc-dev \
+    libxml2-dev \
+    llvm16-dev \
+    llvm16-static \
+    make \
+    musl-dev \
+    openssl-dev \
+    openssl-libs-static \
+    pcre-dev \
+    shards@edge \
+    yaml-dev \
+    yaml-static \
+    zlib-dev \
+    zlib-static \
+"
+
+# setup arch
+case "$TARGET_ARCH" in
+  amd64) DOCKER_IMAGE="alpine" ;;
+  arm64) DOCKER_IMAGE="multiarch/alpine:aarch64-edge" ;;
+  armel) DOCKER_IMAGE="multiarch/alpine:armv7-edge" ;;
+  # armhf) DOCKER_IMAGE="multiarch/alpine:armhf-edge" ;;
+  # i386)  DOCKER_IMAGE="multiarch/alpine:x86-edge" ;;
+  mips)  DOCKER_IMAGE="multiarch/alpine:mips-edge" ;;
+  mipsel)  DOCKER_IMAGE="multiarch/alpine:mipsel-edge" ;;
+  powerpc)  DOCKER_IMAGE="multiarch/alpine:powerpc-edge" ;;
+  ppc64el)  DOCKER_IMAGE="multiarch/alpine:ppc64el-edge" ;;
+  s390x)  DOCKER_IMAGE="multiarch/alpine:s390x-edge" ;;
+esac
+
+# Compile Crystal project statically for target architecture
+docker pull multiarch/qemu-user-static:register
+docker run \
+  --rm \
+  --privileged \
+  multiarch/qemu-user-static:register \
+  --reset
+docker run \
+  -it \
+  -v "$LOCAL_PROJECT_PATH:/app" \
+  -w /app \
+  --rm \
+  "$DOCKER_IMAGE" \
+  /bin/sh -c "$INSTALL_CRYSTAL && $BUILD_COMMAND"
+
diff --git a/scripts/ci.crossbuild-debian.sh b/scripts/ci.crossbuild-debian.sh
new file mode 100755
index 0000000..cfa6189
--- /dev/null
+++ b/scripts/ci.crossbuild-debian.sh
@@ -0,0 +1,82 @@
+#!/bin/sh -eu
+# vim: set ts=2 sw=2 et:
+
+LOCAL_PROJECT_PATH="${1-$PWD}"
+
+TARGET_ARCH="${2-amd64}"
+
+DOCKER_IMAGE=""
+
+BUILD_COMMAND=" \
+  shards build --static --release \
+  && chown 1000:1000 -R bin \
+  && find bin -type f -maxdepth 1 -exec mv {} {}_${TARGET_ARCH} \; \
+"
+
+# crystal
+INSTALL_CRYSTAL=" \
+  sed -i -e 's/Types: deb/Types: deb deb-src/' /etc/apt/sources.list.d/debian.sources \
+  && echo 'deb http://deb.debian.org/debian unstable main' > /etc/apt/sources.list.d/sid.list \
+  && echo 'deb-src http://deb.debian.org/debian unstable main' >> /etc/apt/sources.list.d/sid.list \
+  && apt-get update \
+  && apt-get install -y \
+    g++ \
+    libxml2-dev \
+    llvm-dev \
+    make \
+    libssl-dev \
+    libpcre3-dev \
+    libyaml-dev \
+    zlib1g-dev \
+    dpkg-dev \
+    debuild \
+  && apt source crystal \
+  && apt build-dep crystal \
+  && ls -lF \
+  && debuild -b -uc -us \
+"
+
+# setup arch
+case "$TARGET_ARCH" in
+  amd64) DOCKER_IMAGE="debian" ;;
+  arm64) DOCKER_IMAGE="arm64v8/debian" ;;
+  armel) DOCKER_IMAGE="arm32v7/debian" ;;
+  armhf) DOCKER_IMAGE="armhf/debian" ;;
+  i386)  DOCKER_IMAGE="x86/debian" ;;
+  mips)  DOCKER_IMAGE="mips/debian" ;;
+  mipsel)  DOCKER_IMAGE="mipsel/debian" ;;
+  powerpc)  DOCKER_IMAGE="powerpc/debian" ;;
+  ppc64el)  DOCKER_IMAGE="ppc64el/debian" ;;
+  s390x)  DOCKER_IMAGE="s390x/debian" ;;
+esac
+
+# Compile Crystal project statically for target architecture
+docker pull multiarch/qemu-user-static
+
+docker run \
+  --rm \
+  --privileged \
+  multiarch/qemu-user-static \
+  --reset -p yes
+
+set -x
+docker run \
+  -it \
+  -v "$LOCAL_PROJECT_PATH:/app" \
+  -w /app \
+  --rm \
+  --platform linux/arm64 \
+  "$DOCKER_IMAGE"
+
+exit 0
+
+set -x
+docker run \
+  -it \
+  -v "$LOCAL_PROJECT_PATH:/app" \
+  -w /app \
+  --rm \
+  --platform linux/arm64 \
+  "$DOCKER_IMAGE" \
+  /bin/sh -c "$INSTALL_CRYSTAL && $BUILD_COMMAND"
+
diff --git a/scripts/compiler.crossbuild-debian.sh b/scripts/compiler.crossbuild-debian.sh
new file mode 100755
index 0000000..54d8a6e
--- /dev/null
+++ b/scripts/compiler.crossbuild-debian.sh
@@ -0,0 +1,86 @@
+#!/bin/sh -eu
+# vim: set ts=2 sw=2 et:
+
+LOCAL_PROJECT_PATH="${1-$PWD}"
+
+TARGET_ARCH="${2-arm64}"
+
+DOCKER_IMAGE=""
+
+BUILD_COMMAND=" \
+  shards build --static --release \
+  && chown 1000:1000 -R bin \
+  && find bin -type f -maxdepth 1 -exec mv {} {}_${TARGET_ARCH} \; \
+"
+
+# crystal
+INSTALL_CRYSTAL=" \
+  sed -i -e '/^deb/d' /etc/apt/sources.list \
+  && sed -i -e '/jessie.updates/d' /etc/apt/sources.list \
+  && sed -i -e 's/^# deb/deb/' /etc/apt/sources.list \
+  && apt-get update"
+
+cat > /dev/null <<EOF
+"
+  && apt-get install -y \
+    g\+\+ \
+    gcc \
+    curl \
+    autoconf \
+    automake \
+    python2 \
+    libxml2-dev \
+    llvm-dev \
+    make \
+    libssl-dev \
+    libpcre2-dev \
+    libyaml-dev \
+    zlib1g-dev \
+"
+EOF
+
+# setup arch
+case "$TARGET_ARCH" in
+  amd64) DOCKER_IMAGE="debian:8" ;;
+  arm64) DOCKER_IMAGE="arm64v8/debian:8" ;;
+  armel) DOCKER_IMAGE="arm32v7/debian" ;;
+  armhf) DOCKER_IMAGE="armhf/debian" ;;
+  i386)  DOCKER_IMAGE="x86/debian" ;;
+  mips)  DOCKER_IMAGE="mips/debian" ;;
+  mipsel)  DOCKER_IMAGE="mipsel/debian" ;;
+  powerpc)  DOCKER_IMAGE="powerpc/debian" ;;
+  ppc64el)  DOCKER_IMAGE="ppc64el/debian" ;;
+  s390x)  DOCKER_IMAGE="s390x/debian" ;;
+esac
+
+# Compile Crystal project statically for target architecture
+docker pull multiarch/qemu-user-static
+
+docker run \
+  --rm \
+  --privileged \
+  multiarch/qemu-user-static \
+  --reset -p yes
+
+set -x
+docker run \
+  -it \
+  -v "$LOCAL_PROJECT_PATH:/app" \
+  -w /app \
+  --rm \
+  --platform linux/arm64 \
+  "$DOCKER_IMAGE" \
+  /bin/sh -c "$INSTALL_CRYSTAL && bash"
+
+exit 0
+
+set -x
+docker run \
+  -it \
+  -v "$LOCAL_PROJECT_PATH:/app" \
+  -w /app \
+  --rm \
+  --platform linux/arm64 \
+  "$DOCKER_IMAGE" \
+  /bin/sh -c "$INSTALL_CRYSTAL && $BUILD_COMMAND"
+
diff --git a/scripts/vagrant-provision/base.sh b/scripts/vagrant-provision/base.sh
new file mode 100644
index 0000000..4a0d514
--- /dev/null
+++ b/scripts/vagrant-provision/base.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -e
+set -u
+
+USER="$(test -d /vagrant && echo "vagrant" || echo "debian")"
+HOSTNAME="$(hostname)"
+
+export DEBIAN_FRONTEND=noninteractive
+
+echo "Installing required system packages"
+apt-get update --allow-releaseinfo-change
+apt-get install -y \
+   apt-transport-https \
+   ca-certificates \
+   git \
+   curl \
+   wget \
+   vim \
+   gnupg2 \
+   software-properties-common
+
+# echo "Installing mfm requirements"
+# apt-get install -y \
+#    fzf \
+#    sshfs \
+#    httpdirfs \
+#    libyaml-0-2 \
+#    libyaml-dev \
+#    libpcre3-dev \
+#    libevent-dev
+
diff --git a/scripts/vagrant.provision.sh b/scripts/vagrant-provision/build.sh
similarity index 52%
rename from scripts/vagrant.provision.sh
rename to scripts/vagrant-provision/build.sh
index a074a0a..6317e22 100644
--- a/scripts/vagrant.provision.sh
+++ b/scripts/vagrant-provision/build.sh
@@ -9,26 +9,6 @@ HOSTNAME="$(hostname)"
 
 export DEBIAN_FRONTEND=noninteractive
 
-echo "Installing required system packages"
-apt-get update --allow-releaseinfo-change
-apt-get install -y \
-   apt-transport-https \
-   ca-certificates \
-   git \
-   curl \
-   wget \
-   vim \
-   gnupg2 \
-   software-properties-common
-
-echo "Installing recording requirements"
-apt-get install -y \
-   tmux \
-   mdp \
-   bat \
-   asciinema \
-   termtosvg
-
 echo "Installing mfm requirements"
 apt-get install -y \
    fzf \
@@ -39,24 +19,6 @@ apt-get install -y \
    libpcre3-dev \
    libevent-dev
 
-#!/bin/sh
-
-set -e
-set -u
-
-USER="$(test -d /vagrant && echo "vagrant" || echo "debian")"
-CLUSTERS_DIR=/home/$USER/clusters
-
-# Installation de kompose
-if [ ! -f /usr/local/bin/kompose ]; then
-	DL="$(mktemp)"
-	curl \
-		-L https://github.com/kubernetes/kompose/releases/download/v1.22.0/kompose-linux-amd64 \
-		-o "$DL"
-	chmod +x "$DL"
-	mv "$DL" /usr/local/bin/kompose
-fi
-
 # Installing asdf
 su - "$USER" -c "git config --global advice.detachedHead false"
 su - "$USER" -c "rm -rf ~/.asdf"
diff --git a/scripts/vagrant-provision/recording.sh b/scripts/vagrant-provision/recording.sh
new file mode 100644
index 0000000..416b802
--- /dev/null
+++ b/scripts/vagrant-provision/recording.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# install crystal
+set -e
+set -u
+
+USER="$(test -d /vagrant && echo "vagrant" || echo "debian")"
+HOSTNAME="$(hostname)"
+
+export DEBIAN_FRONTEND=noninteractive
+
+echo "Installing required system packages"
+apt-get update --allow-releaseinfo-change
+
+echo "Installing recording requirements"
+apt-get install -y \
+   tmux \
+   mdp \
+   bat \
+   asciinema \
+   termtosvg
+
diff --git a/shard.lock b/shard.lock
index 308912c..c0f0724 100644
--- a/shard.lock
+++ b/shard.lock
@@ -1,5 +1,9 @@
 version: 2.0
 shards:
+  crinja:
+    git: https://github.com/straight-shoota/crinja.git
+    version: 0.8.1
+
   shellwords:
     git: https://github.com/sztheory/shellwords-crystal.git
     version: 0.1.0
diff --git a/shard.yml b/shard.yml
index 314293a..746f2c8 100644
--- a/shard.yml
+++ b/shard.yml
@@ -18,6 +18,8 @@ targets:
 #   Short description of gx-vault
 
 dependencies:
+  crinja:
+    github: straight-shoota/crinja
   shellwords:
     github: szTheory/shellwords-crystal
 
diff --git a/src/cli.cr b/src/cli.cr
index f60da81..48b3393 100644
--- a/src/cli.cr
+++ b/src/cli.cr
@@ -8,7 +8,10 @@ require "./config"
 require "./fzf"
 
 module GX
+  VERSION="v0.1.9"
+
   class Cli
+    Log = ::Log.for("cli")
 
     @config : Config
 
@@ -27,65 +30,87 @@ module GX
         parser.banner = "Usage: #{PROGRAM_NAME} [options]\n\nGlobal options"
 
         parser.on("-c", "--config FILE", "Set configuration file") do |path|
+          Log.info { "Configuration set to #{path}" }
           @config.path = path
         end
+
+        parser.on("-v", "--verbose", "Set more verbosity") do |flag|
+          Log.info { "Verbosity enabled" }
+          @config.verbose = true
+        end
+
+        parser.on("--version", "Show version") do |flag|
+          @config.mode = Config::Mode::ShowVersion
+        end
+
         parser.on("-h", "--help", "Show this help") do |flag|
           STDOUT.puts parser
           exit(0)
         end
 
         parser.separator("\nCommands")
-        parser.on("create", "Create vault") do 
-          @config.mode = Config::Mode::Add
+        parser.on("config", "Manage configuration") do 
+          parser.banner = "Usage: #{PROGRAM_NAME} config [commands] [options]\n\nGlobal options"
+          parser.separator("\nCommands")
 
-          parser.banner = "Usage: #{PROGRAM_NAME} create [options]\n\nGlobal options"
-          parser.separator("\nCommand options")
+          parser.on("create", "Create vault") do 
+            @config.mode = Config::Mode::ConfigAdd
 
-          parser.on("-n", "--name", "Set vault name") do |name|
-            add_args = add_args.merge({ name: name })
+            parser.banner = "Usage: #{PROGRAM_NAME} config create [commands] [options]\n\nGlobal options"
+            parser.separator("\nCommand options")
+
+            parser.on("-n", "--name", "Set vault name") do |name|
+              add_args = add_args.merge({ name: name })
+            end
+            parser.on("-p", "--path", "Set vault encrypted path") do |path|
+              add_args = add_args.merge({ path: path })
+            end
           end
-          parser.on("-p", "--path", "Set vault encrypted path") do |path|
-            add_args = add_args.merge({ path: path })
+
+          parser.on("delete", "Delete vault") do 
+            @config.mode = Config::Mode::ConfigAdd
+
+            parser.banner = "Usage: #{PROGRAM_NAME} delete [options]\n\nGlobal options"
+            parser.separator("\nCommand options")
+
+            parser.on("-n", "--name", "Set vault name") do |name|
+              delete_args = delete_args.merge({ name: name })
+            end
           end
-        end
 
-        parser.on("delete", "Delete vault") do 
-          @config.mode = Config::Mode::Add
-
-          parser.banner = "Usage: #{PROGRAM_NAME} delete [options]\n\nGlobal options"
-          parser.separator("\nCommand options")
-
-          parser.on("-n", "--name", "Set vault name") do |name|
-            delete_args = delete_args.merge({ name: name })
+          parser.on("edit", "Edit configuration") do |flag|
+            @config.mode = Config::Mode::ConfigEdit
           end
         end
 
-        parser.on("edit", "Edit configuration") do |flag|
-          @config.mode = Config::Mode::Edit
-        end
-
       end
       pparser.parse(args)
     end
 
     def run()
-      @config.load_from_file
+      case @config.mode 
+      when Config::Mode::ShowVersion
+        STDOUT.puts "#{PROGRAM_NAME} #{VERSION}"
+      when Config::Mode::Mount
+        @config.load_from_file
+        mount
+      end
+    end
 
+    def mount()
       names_display = {} of String => NamedTuple(filesystem: Filesystem, ansi_name: String)
       @config.filesystems.each do |filesystem|
         fs_str = filesystem.type.ljust(12,' ')
-        result_name = 
-          if filesystem.mounted? 
-            "#{fs_str} #{filesystem.name} [open]"
-          else
-            "#{fs_str} #{filesystem.name}"
-          end
-        ansi_name = 
-          if filesystem.mounted? 
-            "#{fs_str.colorize(:dark_gray)} #{filesystem.name} [#{ "open".colorize(:green) }]"
-          else
-            "#{fs_str.colorize(:dark_gray)} #{filesystem.name}"
-          end
+
+        suffix = ""
+        suffix_ansi = ""
+        if filesystem.mounted? 
+          suffix = "[open]"
+          suffix_ansi = "[#{ "open".colorize(:green) }]"
+        end
+
+        result_name = "#{fs_str} #{filesystem.name} #{suffix}".strip
+        ansi_name = "#{fs_str.colorize(:dark_gray)} #{filesystem.name} #{suffix_ansi}".strip
 
         names_display[result_name] = {
           filesystem: filesystem,
@@ -93,7 +118,7 @@ module GX
         }
       end
 
-      result_filesystem_name = Fzf.run(names_display.values.map(&.[:ansi_name]).sort)
+      result_filesystem_name = Fzf.run(names_display.values.map(&.[:ansi_name]).sort).strip
       selected_filesystem = names_display[result_filesystem_name][:filesystem]
       puts ">> #{selected_filesystem.name}".colorize(:yellow)
 
diff --git a/src/config.cr b/src/config.cr
index 18005fc..913c066 100644
--- a/src/config.cr
+++ b/src/config.cr
@@ -3,14 +3,20 @@
 # SPDX-FileCopyrightText: 2023 Glenn Y. Rolland <glenux@glenux.net>
 # Copyright © 2023 Glenn Y. Rolland <glenux@glenux.net>
 
+require "crinja"
+
 require "./filesystems"
 
 module GX
   class Config
+    Log = ::Log.for("config")
+
     enum Mode
-      Add
-      Edit
-      Run
+      ConfigAdd
+      ConfigDelete
+      ConfigEdit
+      ShowVersion
+      Mount
     end
 
     record NoArgs
@@ -19,36 +25,69 @@ module GX
 
     getter filesystems : Array(Filesystem)
     getter home_dir : String
+    property verbose : Bool
     property mode : Mode
-    property path : String
+    property path : String?
     property args : AddArgs.class | DelArgs.class | NoArgs.class
 
-    DEFAULT_CONFIG_PATH = "mfm.yml"
-
     def initialize()
       if !ENV["HOME"]?
         raise "Home directory not found"
       end
       @home_dir = ENV["HOME"]
 
-      @mode = Mode::Run
+      @verbose = false
+      @mode = Mode::Mount
       @filesystems = [] of Filesystem
-      @path = File.join(@home_dir, ".config", DEFAULT_CONFIG_PATH)
+      @path = nil
+
       @args = NoArgs
     end
 
+    def detect_config_file()
+      possible_files = [
+        File.join(@home_dir, ".config", "mfm", "config.yaml"),
+        File.join(@home_dir, ".config", "mfm", "config.yml"),
+        File.join(@home_dir, ".config", "mfm.yaml"),
+        File.join(@home_dir, ".config", "mfm.yml"),
+        File.join("/etc", "mfm", "config.yaml"),
+        File.join("/etc", "mfm", "config.yml"),
+      ]
+
+      possible_files.each do |file_path|
+        if File.exists?(file_path)
+          Log.info { "Configuration file found: #{file_path}" }
+          return file_path if File.exists?(file_path)
+        else
+          Log.debug { "Configuration file not found: #{file_path}" }
+        end
+      end
+
+      Log.error { "No configuration file found in any of the standard locations" }
+      raise "Configuration file not found"
+    end
+
     def load_from_file
+      path = @path
+      if path.nil?
+        path = detect_config_file()
+      end
+      @path = path
       @filesystems = [] of Filesystem
 
-      if !File.exists? @path
-        STDERR.puts "Error: file #{@path} does not exist!".colorize(:red)
+      if !File.exists? path
+        Log.error { "File #{path} does not exist!".colorize(:red) }
         exit(1)
       end
-      load_filesystems(@path)
+      load_filesystems(path)
     end
 
     private def load_filesystems(config_path : String)
-      yaml_data = YAML.parse(File.read(config_path))
+      file_data = File.read(config_path)
+      # FIXME: render template on a value basis (instead of global)
+      file_patched = Crinja.render(file_data, {"env" => ENV.to_h}) 
+
+      yaml_data = YAML.parse(file_patched)
       vaults_data = yaml_data["filesystems"].as_a
 
       vaults_data.each do |filesystem_data|
diff --git a/src/filesystems/sshfs.cr b/src/filesystems/sshfs.cr
index 802639b..44ddab9 100644
--- a/src/filesystems/sshfs.cr
+++ b/src/filesystems/sshfs.cr
@@ -12,6 +12,7 @@ module GX
     getter remote_path : String = ""
     getter remote_user : String = ""
     getter remote_host : String = ""
+    getter remote_port : String = "22"
 
     @[YAML::Field(key: "mount_dir", ignore: true)]
     getter mount_dir : String = ""
@@ -34,7 +35,11 @@ module GX
         error = STDERR
         process = Process.new(
           "sshfs", 
-          ["#{remote_user}@#{remote_host}:#{remote_path}", mount_dir], 
+          [
+            "-p", remote_port,
+            "#{remote_user}@#{remote_host}:#{remote_path}", 
+            mount_dir
+          ], 
           input: input, 
           output: output, 
           error: error
diff --git a/src/main.cr b/src/main.cr
index 43192b0..f32183c 100644
--- a/src/main.cr
+++ b/src/main.cr
@@ -6,11 +6,33 @@
 require "yaml"
 require "colorize"
 require "json"
+require "log"
 
 require "./filesystems/gocryptfs"
 require "./config"
 require "./cli"
 
+struct BaseFormat < Log::StaticFormatter
+  def run
+    string @entry.severity.label.downcase
+    string "("
+    source
+    string "): "
+    message
+  end
+end
+
+Log.setup do |config|
+  backend = Log::IOBackend.new(formatter: BaseFormat)
+  config.bind "*", Log::Severity::Info, backend
+
+  if ENV["LOG_LEVEL"]?
+    level = Log::Severity.parse(ENV["LOG_LEVEL"]) || Log::Severity::Info
+    config.bind "*", level, backend
+  end
+end
+
+
 app = GX::Cli.new
 app.parse_command_line(ARGV)
 app.run