diff --git a/AppImage-Recipe.sh b/AppImage-Recipe.sh
deleted file mode 100755
index 9975f4939..000000000
--- a/AppImage-Recipe.sh
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/usr/bin/env bash
-# 
-# KeePassXC AppImage Recipe
-# Copyright (C) 2017-2018 KeePassXC team <https://keepassxc.org/>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 or (at your option)
-# version 3 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-if [ "$1" == "" ] || [ "$2" == "" ]; then
-    echo "Usage: $(basename $0) APP_NAME RELEASE_VERSION" >&2
-    exit 1
-fi
-
-if [ -f CHANGELOG ]; then
-    echo "This recipe must not be run from the sources root." >&2
-    exit 1
-fi
-
-if [ ! -d ../bin-release ]; then
-    echo "../bin-release does not exist." >&2
-    exit 1
-fi
-
-APP="$1"
-LOWERAPP="$(echo "$APP" | tr '[:upper:]' '[:lower:]')"
-VERSION="$2"
-export ARCH=x86_64
-
-mkdir -p $APP.AppDir
-wget -q https://github.com/AppImage/AppImages/raw/master/functions.sh -O ./functions.sh
-. ./functions.sh
-
-LIB_DIR=./usr/lib
-if [ -d ./usr/lib/x86_64-linux-gnu ]; then
-    LIB_DIR=./usr/lib/x86_64-linux-gnu
-elif [ -d ./usr/lib/i386-linux-gnu ]; then
-    LIB_DIR=./usr/lib/i386-linux-gnu
-elif [ -d ./usr/lib64 ]; then
-    LIB_DIR=./usr/lib64
-fi
-
-cd $APP.AppDir
-cp -a ../../bin-release/* .
-cp -a ./usr/local/* ./usr
-rm -R ./usr/local
-rmdir ./opt 2> /dev/null
-
-# bundle Qt platform plugins and themes
-QXCB_PLUGIN="$(find /usr/lib* -name 'libqxcb.so' 2> /dev/null)"
-if [ "$QXCB_PLUGIN" == "" ]; then
-    QXCB_PLUGIN="$(find /opt/qt*/plugins -name 'libqxcb.so' 2> /dev/null)"
-fi
-QT_PLUGIN_PATH="$(dirname $(dirname $QXCB_PLUGIN))"
-mkdir -p ".${QT_PLUGIN_PATH}/platforms"
-cp -a "$QXCB_PLUGIN" ".${QT_PLUGIN_PATH}/platforms/"
-cp -a "${QT_PLUGIN_PATH}/platforminputcontexts/" ".${QT_PLUGIN_PATH}/platforminputcontexts/"
-cp -a "${QT_PLUGIN_PATH}/imageformats/" ".${QT_PLUGIN_PATH}/imageformats/"
-
-get_apprun
-copy_deps
-
-# protect our libgpg-error from being deleted
-mv ./opt/keepassxc-libs/lib/x86_64-linux-gnu/libgpg-error.so.0 ./protected.so
-delete_blacklisted
-mv ./protected.so ./opt/keepassxc-libs/lib/x86_64-linux-gnu/libgpg-error.so.0
-
-get_desktop
-get_icon
-cat << EOF > ./usr/bin/keepassxc_env
-#!/usr/bin/env bash
-export LD_LIBRARY_PATH="..$(dirname ${QT_PLUGIN_PATH})/lib:\${LD_LIBRARY_PATH}"
-export LD_LIBRARY_PATH="../opt/keepassxc-libs/lib/x86_64-linux-gnu:\${LD_LIBRARY_PATH}"
-
-export QT_PLUGIN_PATH="..${QT_PLUGIN_PATH}:\${KPXC_QT_PLUGIN_PATH}"
-
-# unset XDG_DATA_DIRS to make tray icon work in Ubuntu Unity
-# see https://github.com/AppImage/AppImageKit/issues/351
-unset XDG_DATA_DIRS
-
-if [ "\${1}" == "cli" ]; then
-    shift
-    exec keepassxc-cli "\$@"
-elif [ "\${1}" == "proxy" ]; then
-    shift
-    exec keepassxc-proxy "\$@"
-elif [ -v CHROME_WRAPPER ] || [ -v MOZ_LAUNCHED_CHILD ]; then
-    exec keepassxc-proxy "\$@"
-else
-    exec keepassxc "\$@"
-fi
-EOF
-chmod +x ./usr/bin/keepassxc_env
-sed -i 's/Exec=keepassxc/Exec=keepassxc_env/' org.${LOWERAPP}.${APP}.desktop
-get_desktopintegration "org.${LOWERAPP}.${APP}"
-
-cd ..
-
-GLIBC_NEEDED=$(glibc_needed)
-NO_GLIBC_VERSION=true
-
-generate_type2_appimage -u "gh-releases-zsync|keepassxreboot|keepassxc|latest|KeePassXC-*-${ARCH}.AppImage.zsync"
-
-mv ../out/*.AppImage* ../
-rm -rf ../out
diff --git a/Dockerfile b/Dockerfile
index e6f1a38e3..89ee04464 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -18,8 +18,8 @@ FROM ubuntu:14.04
 
 ENV REBUILD_COUNTER=8
 
-ENV QT5_VERSION=59
-ENV QT5_PPA_VERSION=${QT5_VERSION}4
+ENV QT5_VERSION=qt510
+ENV QT5_PPA_VERSION=qt-5.10.1
 ENV TERM=xterm-256color
 
 RUN set -x \
@@ -27,7 +27,7 @@ RUN set -x \
     && apt-get -y install software-properties-common
 
 RUN set -x \
-    && add-apt-repository ppa:beineri/opt-qt${QT5_PPA_VERSION}-trusty \
+    && add-apt-repository ppa:beineri/opt-${QT5_PPA_VERSION}-trusty \
     && add-apt-repository ppa:phoerious/keepassxc
 
 RUN set -x \
@@ -38,17 +38,18 @@ RUN set -x \
 RUN set -x \
     && apt-get install -y \
         cmake3 \
+        curl \
         g++ \
         git \
         libgcrypt20-18-dev \
         libargon2-0-dev \
         libsodium-dev \
         libcurl-no-gcrypt-dev \
-        qt${QT5_VERSION}base \
-        qt${QT5_VERSION}tools \
-        qt${QT5_VERSION}x11extras \
-        qt${QT5_VERSION}translations \
-        qt${QT5_VERSION}imageformats \
+        ${QT5_VERSION}base \
+        ${QT5_VERSION}tools \
+        ${QT5_VERSION}x11extras \
+        ${QT5_VERSION}translations \
+        ${QT5_VERSION}imageformats \
         zlib1g-dev \
         libxi-dev \
         libxtst-dev \
@@ -56,21 +57,30 @@ RUN set -x \
         libyubikey-dev \
         libykpers-1-dev
 
-ENV CMAKE_PREFIX_PATH="/opt/qt${QT5_VERSION}/lib/cmake"
+ENV PATH="/opt/${QT5_VERSION}/bin:${PATH}"
+ENV CMAKE_PREFIX_PATH="/opt/${QT5_VERSION}/lib/cmake"
 ENV CMAKE_INCLUDE_PATH="/opt/keepassxc-libs/include"
 ENV CMAKE_LIBRARY_PATH="/opt/keepassxc-libs/lib/x86_64-linux-gnu"
 ENV CPATH="${CMAKE_INCLUDE_PATH}"
-ENV LD_LIBRARY_PATH="${CMAKE_LIBRARY_PATH}:/opt/qt${QT5_VERSION}/lib"
+ENV LD_LIBRARY_PATH="${CMAKE_LIBRARY_PATH}:/opt/${QT5_VERSION}/lib"
 
 RUN set -x \
-    && echo "/opt/qt${QT5_VERSION}/lib" > /etc/ld.so.conf.d/qt${QT5_VERSION}.conf \
+    && echo "/opt/${QT5_VERSION}/lib" > /etc/ld.so.conf.d/${QT5_VERSION}.conf \
     && echo "/opt/keepassxc-libs/lib/x86_64-linux-gnu" > /etc/ld.so.conf.d/keepassxc.conf
 
 # AppImage dependencies
 RUN set -x \
     && apt-get install -y \
-        libfuse2 \
-        wget
+        curl \
+        libfuse2
+
+RUN set -x \
+    && curl -L "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" > /usr/bin/linuxdeploy \
+    && curl -L "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" > /usr/bin/linuxdeploy-plugin-qt \
+    && curl -L "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" > /usr/bin/appimagetool \
+    && chmod +x /usr/bin/linuxdeploy \
+    && chmod +x /usr/bin/linuxdeploy-plugin-qt \
+    && chmod +x /usr/bin/appimagetool
 
 RUN set -x \
     && apt-get autoremove --purge \
diff --git a/ci/snapcraft/Dockerfile b/ci/snapcraft/Dockerfile
deleted file mode 100644
index 43a991d0e..000000000
--- a/ci/snapcraft/Dockerfile
+++ /dev/null
@@ -1,51 +0,0 @@
-# KeePassXC Linux Release Build Dockerfile
-# Copyright (C) 2017-2018 KeePassXC team <https://keepassxc.org/>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 2 or (at your option)
-# version 3 of the License.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-FROM snapcore/snapcraft
-
-ENV REBUILD_COUNTER=1
-
-ENV QT5_VERSION=510
-ENV QT5_PPA_VERSION=5.10.1
-ENV TERM=xterm-256color
-
-RUN set -x \
-    && apt update -y \
-    && apt -y install software-properties-common
-
-RUN set -x \
-    && add-apt-repository ppa:phoerious/keepassxc
-
-RUN set -x \
-    && apt update -y \
-    && apt-get -y --no-install-recommends install \
-      build-essential \
-      cmake \
-      libgcrypt20-18-dev \
-      libargon2-0-dev \
-      libsodium-dev \
-      qtbase5-dev \
-      qttools5-dev \
-      qttools5-dev-tools \
-      zlib1g-dev \
-      libyubikey-dev \
-      libykpers-1-dev \
-      libxi-dev \
-      libxtst-dev \
-      xvfb
-
-RUN set -x \
-    && apt-get autoremove --purge
diff --git a/ci/trusty/Dockerfile b/ci/trusty/Dockerfile
index 421e8ca24..04aee25a5 100644
--- a/ci/trusty/Dockerfile
+++ b/ci/trusty/Dockerfile
@@ -20,7 +20,7 @@ FROM ubuntu:14.04
 
 ENV REBUILD_COUNTER=4
 
-ENV QT5_VERSION=53
+ENV QT5_VERSION=qt53
 ENV QT5_PPA_VERSION=${QT5_VERSION}2
 ENV TERM=xterm-256color
 
@@ -29,43 +29,58 @@ RUN set -x \
      && apt-get -y install software-properties-common
 
 RUN set -x \
-    && add-apt-repository ppa:beineri/opt-qt${QT5_PPA_VERSION}-trusty \
+    && add-apt-repository ppa:beineri/opt-${QT5_PPA_VERSION}-trusty \
     && add-apt-repository ppa:phoerious/keepassxc
 
 RUN set -x \
     && apt-get -y update \
     && apt-get -y --no-install-recommends install \
-      build-essential \
-      clang-3.6 \
-      libclang-common-3.6-dev \
-      clang-format-3.6 \
-      cmake3 \
-      make \
-      libgcrypt20-18-dev \
-      libargon2-0-dev \
-      libsodium-dev \
-      libcurl-no-gcrypt-dev \
-      qt${QT5_VERSION}base \
-      qt${QT5_VERSION}tools \
-      qt${QT5_VERSION}x11extras \
-      qt${QT5_VERSION}translations \
-      zlib1g-dev \
-      libyubikey-dev \
-      libykpers-1-dev \
-      libxi-dev \
-      libxtst-dev \
-      xvfb
+        build-essential \
+        clang-3.6 \
+        libclang-common-3.6-dev \
+        clang-format-3.6 \
+        cmake3 \
+        make \
+        libgcrypt20-18-dev \
+        libargon2-0-dev \
+        libsodium-dev \
+        libcurl-no-gcrypt-dev \
+        ${QT5_VERSION}base \
+        ${QT5_VERSION}tools \
+        ${QT5_VERSION}x11extras \
+        ${QT5_VERSION}translations \
+        zlib1g-dev \
+        libyubikey-dev \
+        libykpers-1-dev \
+        libxi-dev \
+        libxtst-dev \
+        xvfb
 
-ENV CMAKE_PREFIX_PATH="/opt/qt${QT5_VERSION}/lib/cmake"
+ENV PATH="/opt/${QT5_VERSION}/bin:${PATH}"
+ENV CMAKE_PREFIX_PATH="/opt/${QT5_VERSION}/lib/cmake"
 ENV CMAKE_INCLUDE_PATH="/opt/keepassxc-libs/include"
 ENV CMAKE_LIBRARY_PATH="/opt/keepassxc-libs/lib/x86_64-linux-gnu"
 ENV CPATH="${CMAKE_INCLUDE_PATH}"
-ENV LD_LIBRARY_PATH="${CMAKE_LIBRARY_PATH}:/opt/qt${QT5_VERSION}/lib"
+ENV LD_LIBRARY_PATH="${CMAKE_LIBRARY_PATH}:/opt/${QT5_VERSION}/lib"
 
 RUN set -x \
-    && echo "/opt/qt${QT5_VERSION}/lib" > /etc/ld.so.conf.d/qt${QT5_VERSION}.conf \
+    && echo "/opt/${QT5_VERSION}/lib" > /etc/ld.so.conf.d/${QT5_VERSION}.conf \
     && echo "/opt/keepassxc-libs/lib/x86_64-linux-gnu" > /etc/ld.so.conf.d/keepassxc.conf
 
+# AppImage dependencies
+RUN set -x \
+    && apt-get install -y \
+        curl \
+        libfuse2
+
+RUN set -x \
+    && curl -L "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" > /usr/bin/linuxdeploy \
+    && curl -L "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" > /usr/bin/linuxdeploy-plugin-qt \
+    && curl -L "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" > /usr/bin/appimagetool \
+    && chmod +x /usr/bin/linuxdeploy \
+    && chmod +x /usr/bin/linuxdeploy-plugin-qt \
+    && chmod +x /usr/bin/appimagetool
+
 RUN set -x \
     && apt-get autoremove --purge \
     && rm -rf /var/lib/apt/lists/*
diff --git a/release-tool b/release-tool
index e70a1db39..885a5d2a0 100755
--- a/release-tool
+++ b/release-tool
@@ -1,5 +1,5 @@
 #!/usr/bin/env bash
-#  
+#
 # KeePassXC Release Preparation Helper
 # Copyright (C) 2017 KeePassXC team <https://keepassxc.org/>
 #
@@ -50,7 +50,8 @@ printUsage() {
     local cmd
     if [ "" == "$1" ] || [ "help" == "$1" ]; then
         cmd="COMMAND"
-    elif [ "check" == "$1" ] || [ "merge" == "$1" ] || [ "build" == "$1" ] || [ "gpgsign" == "$1" ] || [ "appsign" == "$1" ]; then
+    elif [ "check" == "$1" ] || [ "merge" == "$1" ] || [ "build" == "$1" ] \
+        || [ "gpgsign" == "$1" ] || [ "appsign" == "$1" ] || [ "appimage" == "$1" ]; then
         cmd="$1"
     else
         logError "Unknown command: '$1'\n"
@@ -58,7 +59,7 @@ printUsage() {
     fi
 
     printf "\e[1mUsage:\e[0m $(basename $0) $cmd [options]\n"
-    
+
     if [ "COMMAND" == "$cmd" ]; then
         cat << EOF
 
@@ -107,6 +108,8 @@ Options:
                           The container must not exist already
       --snapcraft         Create and use docker image to build snapcraft distribution.
                           This option has no effect if --docker-image is not set.
+      --appimage          Build a Linux AppImage after compilation.
+                          If this option is set, --install-prefix has no effect
       --appsign           Perform platform specific App Signing before packaging
   -k, --key               Specify the App Signing Key/Identity
   -c, --cmake-options     Additional CMake options for compiling the sources
@@ -139,6 +142,25 @@ Options:
   -f, --files        Files to sign (required)
   -k, --key          Signing Key or Apple Developer ID
   -h, --help         Show this help
+EOF
+    elif [ "appimage" == "$cmd" ]; then
+        cat << EOF
+
+Generate Linux AppImage from 'make install' AppDir
+
+Options:
+  -a, --appdir         Input AppDir (required)
+  -v, --version        KeePassXC version
+  -o, --output-dir     Output directory where to build the AppImage
+                       (default: '${OUTPUT_DIR}')
+  -d, --docker-image   Use the specified Docker image to build the AppImage.
+                       The image must have all required build dependencies installed.
+      --container-name Docker container name (default: '${DOCKER_CONTAINER_NAME}')
+                       The container must not exist already
+      --appsign        Embed a PGP signature into the AppImage
+  -k, --key            The PGP Signing Key
+      --verbosity      linuxdeploy verbosity (default: 3)
+  -h, --help           Show this help
 EOF
     fi
 }
@@ -161,7 +183,7 @@ init() {
     if [ "" == "$TAG_NAME" ]; then
         TAG_NAME="$RELEASE_NAME"
     fi
-    
+
     if [ "" == "$SOURCE_BRANCH" ]; then
         SOURCE_BRANCH="release/${RELEASE_NAME}"
     fi
@@ -192,6 +214,10 @@ exitTrap() {
     exitError "Existing upon user request..."
 }
 
+cmdExists() {
+    command -v "$1" &> /dev/null
+}
+
 checkSourceDirExists() {
     if [ ! -d "$SRC_DIR" ]; then
         exitError "Source directory '${SRC_DIR}' does not exist!"
@@ -271,7 +297,7 @@ checkChangeLog() {
     if [ ! -f CHANGELOG ]; then
         exitError "No CHANGELOG file found!"
     fi
-    
+
     grep -qPzo "${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n=+\n" CHANGELOG
     if [ $? -ne 0 ]; then
         exitError "'CHANGELOG' has not been updated to the '${RELEASE_NAME}' release!"
@@ -302,38 +328,32 @@ checkSnapcraft() {
 }
 
 checkTransifexCommandExists() {
-    command -v tx > /dev/null
-    if [ 0 -ne $? ]; then
+    if ! cmdExists tx; then
         exitError "Transifex tool 'tx' not installed! Please install it using 'pip install transifex-client'."
     fi
 }
 
 checkOsslsigncodeCommandExists() {
-    command -v osslsigncode > /dev/null
-    if [ 0 -ne $? ]; then
+    if ! cmdExists osslsigncode; then
         exitError "osslsigncode command not found on the PATH! Please install it using 'pacman -S mingw-w64-osslsigncode'."
     fi
 }
 
 checkSigntoolCommandExists() {
-    command -v signtool > /dev/null
-    if [ 0 -ne $? ]; then
+    if ! cmdExists signtool; then
         exitError "signtool command not found on the PATH! Add the Windows SDK binary folder to your PATH."
     fi
 }
 
 checkCodesignCommandExists() {
-    command -v codesign > /dev/null
-    if [ 0 -ne $? ]; then
+    if !cmdExists codesign; then
         exitError "codesign command not found on the PATH! Please check that you have correctly installed Xcode."
     fi
 }
 
 checkQt5LUpdateExists() {
-    command -v lupdate > /dev/null
-    if [ 0 -eq $? ] && ! $(lupdate -version | grep -q "lupdate version 5\."); then
-        command -v lupdate-qt5 > /dev/null
-        if [ 0 -ne $? ]; then
+    if cmdExists lupdate && ! $(lupdate -version | grep -q "lupdate version 5\."); then
+        if ! cmdExists lupdate-qt5; then
             exitError "Qt Linguist tool (lupdate-qt5) is not installed! Please install using 'apt install qttools5-dev-tools'"
         fi
     fi
@@ -341,12 +361,12 @@ checkQt5LUpdateExists() {
 
 performChecks() {
     logInfo "Performing basic checks..."
-    
+
     checkSourceDirExists
 
     logInfo "Changing to source directory..."
     cd "${SRC_DIR}"
-    
+
     logInfo "Validating toolset and repository..."
 
     checkTransifexCommandExists
@@ -356,23 +376,23 @@ performChecks() {
     checkWorkingTreeClean
     checkSourceBranchExists
     checkTargetBranchExists
-    
+
     logInfo "Checking out '${SOURCE_BRANCH}'..."
     git checkout "$SOURCE_BRANCH"
-    
+
     logInfo "Attempting to find '${RELEASE_NAME}' in various files..."
 
     checkVersionInCMake
     checkChangeLog
     checkAppStreamInfo
     checkSnapcraft
-    
+
     logInfo "\e[1m\e[32mAll checks passed!\e[0m"
 }
 
 # re-implement realpath for OS X (thanks mschrag)
 # https://superuser.com/questions/205127/
-if ! $(command -v realpath > /dev/null); then
+if ! cmdExists realpath; then
     realpath() {
         pushd . > /dev/null
         if [ -d "$1" ]; then
@@ -381,7 +401,7 @@ if ! $(command -v realpath > /dev/null); then
         else
             cd "$(dirname "$1")"
             cur_dir=$(dirs -l +0)
-            
+
             if [ "$cur_dir" == "/" ]; then
                 echo "$cur_dir$(basename "$1")"
             else
@@ -421,42 +441,42 @@ check() {
 # -----------------------------------------------------------------------
 #                             merge command
 # -----------------------------------------------------------------------
-merge() {    
+merge() {
     while [ $# -ge 1 ]; do
         local arg="$1"
         case "$arg" in
             -v|--version)
                 RELEASE_NAME="$2"
                 shift ;;
-                
+
             -a|--app-name)
                 APP_NAME="$2"
                 shift ;;
-            
+
             -s|--source-dir)
                 SRC_DIR="$2"
                 shift ;;
-            
+
             -k|--key|-g|--gpg-key)
                 GPG_GIT_KEY="$2"
                 shift ;;
-            
+
             -r|--release-branch)
                 SOURCE_BRANCH="$2"
                 shift ;;
-            
+
             --target-branch)
                 TARGET_BRANCH="$2"
                 shift ;;
-            
+
             -t|--tag-name)
                 TAG_NAME="$2"
                 shift ;;
-            
+
             -h|--help)
                 printUsage "merge"
                 exit ;;
-            
+
             *)
                 logError "Unknown option '$arg'\n"
                 printUsage "merge"
@@ -468,7 +488,7 @@ merge() {
     init
 
     performChecks
-    
+
     logInfo "Updating language files..."
     ./share/translations/update.sh update
     ./share/translations/update.sh pull
@@ -489,10 +509,10 @@ merge() {
     CHANGELOG=$(grep -Pzo "(?<=${RELEASE_NAME} \(\d{4}-\d{2}-\d{2}\)\n)=+\n\n?(?:.|\n)+?\n(?=\n)" \
         CHANGELOG | grep -Pzo '(?<=\n\n)(.|\n)+' | tr -d \\0)
     COMMIT_MSG="Release ${RELEASE_NAME}"
-    
+
     logInfo "Checking out target branch '${TARGET_BRANCH}'..."
     git checkout "$TARGET_BRANCH"
-    
+
     logInfo "Merging '${SOURCE_BRANCH}' into '${TARGET_BRANCH}'..."
 
     git merge "$SOURCE_BRANCH" --no-ff -m "$COMMIT_MSG" -m "${CHANGELOG}" "$SOURCE_BRANCH" -S"$GPG_GIT_KEY"
@@ -503,14 +523,203 @@ merge() {
     else
         git tag -a "$TAG_NAME" -m "$COMMIT_MSG" -m "${CHANGELOG}" -s -u "$GPG_GIT_KEY"
     fi
-    
+
     cleanup
-    
+
     logInfo "All done!"
     logInfo "Please merge the release branch back into the develop branch now and then push your changes."
     logInfo "Don't forget to also push the tags using \e[1mgit push --tags\e[0m."
 }
 
+# -----------------------------------------------------------------------
+#                         appimage command
+# -----------------------------------------------------------------------
+appimage() {
+    local appdir
+    local build_appsign=false
+    local build_key
+    local verbosity="1"
+
+    while [ $# -ge 1 ]; do
+        local arg="$1"
+        case "$arg" in
+            -v|--version)
+                RELEASE_NAME="$2"
+                shift ;;
+
+            -a|--appdir)
+                appdir="$2"
+                shift ;;
+
+            -o|--output-dir)
+                OUTPUT_DIR="$2"
+                shift ;;
+
+            -d|--docker-image)
+                DOCKER_IMAGE="$2"
+                shift ;;
+
+            --container-name)
+                DOCKER_CONTAINER_NAME="$2"
+                shift ;;
+
+            --appsign)
+                build_appsign=true ;;
+
+            --verbosity)
+                verbosity=$2
+                shift ;;
+
+            -k|--key)
+                build_key="$2"
+                shift ;;
+
+            -h|--help)
+                printUsage "appimage"
+                exit ;;
+
+            *)
+                logError "Unknown option '$arg'\n"
+                printUsage "appimage"
+                exit 1 ;;
+        esac
+        shift
+    done
+
+    if [ -z "${appdir}" ]; then
+        logError "Missing arguments, --appdir is required!\n"
+        printUsage "appimage"
+        exit 1
+    fi
+
+    if [ ! -d "${appdir}" ]; then
+        logError "AppDir does not exist, please create one with 'make install'!\n"
+        exit 1
+    elif [ -e "${appdir}/AppRun" ]; then
+        logError "AppDir has already been run through linuxdeploy, please create a fresh AppDir with 'make install'.\n"
+        exit 1
+    fi
+
+    appdir="$(realpath "$appdir")"
+
+    local out="${OUTPUT_DIR}"
+    if [ "" == "$out" ]; then
+        out="."
+    fi
+    mkdir -p "$out"
+    local out_real="$(realpath "$out")"
+    cd "$out"
+
+    local linuxdeploy="linuxdeploy"
+    local linuxdeploy_cleanup
+    local linuxdeploy_plugin_qt="linuxdeploy-plugin-qt"
+    local linuxdeploy_plugin_qt_cleanup
+    local appimagetool="appimagetool"
+    local appimagetool_cleanup
+
+    logInfo "Testing for AppImage tools..."
+    local docker_test_cmd
+    if [ "" != "$DOCKER_IMAGE" ]; then
+        docker_test_cmd="docker run --rm ${DOCKER_IMAGE}"
+    fi
+
+    # Test if linuxdeploy and linuxdeploy-plugin-qt are installed
+    # on the system or inside the Docker container
+    if ! ${docker_test_cmd} which ${linuxdeploy} &> /dev/null; then
+        logInfo "Downloading linuxdeploy..."
+        linuxdeploy="./linuxdeploy"
+        linuxdeploy_cleanup="rm -f ${linuxdeploy}"
+        curl -L "https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage" > "$linuxdeploy"
+        chmod +x "$linuxdeploy"
+    fi
+    if ! ${docker_test_cmd} which ${linuxdeploy_plugin_qt} &> /dev/null; then
+        logInfo "Downloading linuxdeploy-plugin-qt..."
+        linuxdeploy_plugin_qt="./linuxdeploy-plugin-qt"
+        linuxdeploy_plugin_qt_cleanup="rm -f ${linuxdeploy_plugin_qt}"
+        curl -L "https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage" > "$linuxdeploy_plugin_qt"
+        chmod +x "$linuxdeploy_plugin_qt"
+    fi
+
+    # appimagetool is always run outside a Docker container, so we can access our GPG keys
+    if ! cmdExists ${appimagetool}; then
+        logInfo "Downloading appimagetool..."
+        appimagetool="./appimagetool"
+        appimagetool_cleanup="rm -f ${appimagetool}"
+        curl -L "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage" > "$appimagetool"
+        chmod +x "$appimagetool"
+    fi
+
+    # Create custom AppRun wrapper
+    cat << EOF > "${out_real}/KeePassXC-AppRun"
+#!/usr/bin/env bash
+
+export PATH="\$(dirname \$0)/usr/bin:\${PATH}"
+export LD_LIBRARY_PATH="\$(dirname \$0)/usr/lib:\${LD_LIBRARY_PATH}"
+
+if [ "\${1}" == "cli" ]; then
+    shift
+    exec keepassxc-cli "\$@"
+elif [ "\${1}" == "proxy" ]; then
+    shift
+    exec keepassxc-proxy "\$@"
+elif [ -v CHROME_WRAPPER ] || [ -v MOZ_LAUNCHED_CHILD ]; then
+    exec keepassxc-proxy "\$@"
+else
+    exec keepassxc "\$@"
+fi
+EOF
+    chmod +x "${out_real}/KeePassXC-AppRun"
+
+    # Find .desktop files, icons, and binaries to deploy
+    local desktop_file="$(find "$appdir" -name "org.keepassxc.KeePassXC.desktop" | head -n1)"
+    local icon="$(find "$appdir" -name 'keepassxc.png' | grep -P 'application/256x256/apps/keepassxc.png$' | head -n1)"
+    local executables="$(IFS=$'\n' find "$appdir" | grep -P '/usr/bin/keepassxc[^/]*$' | xargs -i printf " --executable={}")"
+
+    logInfo "Collecting libs and patching binaries..."
+    if [ "" == "$DOCKER_IMAGE" ]; then
+        "$linuxdeploy" --verbosity=${verbosity} --plugin=qt --appdir="$appdir" --desktop-file="$desktop_file" \
+        --custom-apprun="${out_real}/KeePassXC-AppRun" --icon-file="$icon" ${executables} \
+        --library=$(ldconfig -p | grep x86-64 | grep -oP '/[^\s]+/libgpg-error\.so\.\d+$' | head -n1)
+    else
+        desktop_file="${desktop_file//${appdir}/\/keepassxc\/AppDir}"
+        icon="${icon//${appdir}/\/keepassxc\/AppDir}"
+        executables="${executables//${appdir}/\/keepassxc\/AppDir}"
+
+        docker run --name "$DOCKER_CONTAINER_NAME" --rm \
+            --cap-add SYS_ADMIN --security-opt apparmor:unconfined --device /dev/fuse \
+            -v "${appdir}:/keepassxc/AppDir:rw" \
+            -v "${out_real}:/keepassxc/out:rw" \
+            "$DOCKER_IMAGE" \
+            bash -c "cd /keepassxc/out && ${linuxdeploy} --verbosity=${verbosity} --plugin=qt --appdir=/keepassxc/AppDir \
+            --custom-apprun="/keepassxc/out/KeePassXC-AppRun" --desktop-file=${desktop_file} --icon-file=${icon} ${executables} \
+            --library=\$(ldconfig -p | grep x86-64 | grep -oP '/[^\s]+/libgpg-error\.so\.\d+$' | head -n1)"
+    fi
+
+    logInfo "Creating AppImage..."
+    local appsign_flag=""
+    local appsign_key_flag=""
+    if ${build_appsign}; then
+        appsign_flag="--sign"
+        appsign_key_flag="--sign-key ${build_key}"
+    fi
+    local appimage_name="KeePassXC-x86_64.AppImage"
+    if [ "" != "$RELEASE_NAME" ]; then
+        appimage_name="KeePassXC-${RELEASE_NAME}-x86_64.AppImage"
+    fi
+
+    # Run appimagetool to package (and possibly sign) AppImage
+    # --no-appstream is required, since it may crash on newer systems
+    # see: https://github.com/AppImage/AppImageKit/issues/856
+    "$appimagetool" --updateinformation "gh-releases-zsync|keepassxreboot|keepassxc|latest|KeePassXC-*-x86_64.AppImage.zsync" \
+        ${appsign_flag} ${appsign_key_flag} --no-appstream "$appdir" "${out_real}/${appimage_name}"
+
+    logInfo "Cleaning up temporary files..."
+    ${linuxdeploy_cleanup}
+    ${linuxdeploy_plugin_qt_cleanup}
+    ${appimagetool_cleanup}
+    rm -f "${out_real}/KeePassXC-AppRun"
+}
+
 # -----------------------------------------------------------------------
 #                             build command
 # -----------------------------------------------------------------------
@@ -518,59 +727,63 @@ build() {
     local build_source_tarball=true
     local build_snapshot=false
     local build_snapcraft=false
+    local build_appimage=false
     local build_generators=""
     local build_appsign=false
     local build_key=""
-    
+
     while [ $# -ge 1 ]; do
         local arg="$1"
         case "$arg" in
             -v|--version)
                 RELEASE_NAME="$2"
                 shift ;;
-                
+
             -a|--app-name)
                 APP_NAME="$2"
                 shift ;;
-            
+
             -s|--source-dir)
                 SRC_DIR="$2"
                 shift ;;
-            
+
             -o|--output-dir)
                 OUTPUT_DIR="$2"
                 shift ;;
-            
+
             -t|--tag-name)
                 TAG_NAME="$2"
                 shift ;;
-            
+
             -d|--docker-image)
                 DOCKER_IMAGE="$2"
                 shift ;;
 
+            --container-name)
+                DOCKER_CONTAINER_NAME="$2"
+                shift ;;
+
             --appsign)
                 build_appsign=true ;;
 
             -k|--key)
                 build_key="$2"
                 shift ;;
-            
-            --container-name)
-                DOCKER_CONTAINER_NAME="$2"
-                shift ;;
-                
+
             --snapcraft)
                 build_snapcraft=true ;;
-            
+
+            --appimage)
+                build_appimage=true ;;
+
             -c|--cmake-options)
                 CMAKE_OPTIONS="$2"
                 shift ;;
-            
+
             --compiler)
                 COMPILER="$2"
                 shift ;;
-            
+
             -m|--make-options)
                 MAKE_OPTIONS="$2"
                 shift ;;
@@ -578,25 +791,25 @@ build() {
             -g|--generators)
                 build_generators="$2"
                 shift ;;
-            
+
             -i|--install-prefix)
                 INSTALL_PREFIX="$2"
                 shift ;;
-            
+
             -p|--plugins)
                 BUILD_PLUGINS="$2"
                 shift ;;
-            
+
             -n|--no-source-tarball)
                 build_source_tarball=false ;;
 
             --snapshot)
                 build_snapshot=true ;;
-            
+
             -h|--help)
                 printUsage "build"
                 exit ;;
-            
+
             *)
                 logError "Unknown option '$arg'\n"
                 printUsage "build"
@@ -666,19 +879,24 @@ build() {
     logInfo "Creating build directory..."
     mkdir -p "${OUTPUT_DIR}/build-release"
     cd "${OUTPUT_DIR}/build-release"
-    
+
     logInfo "Configuring sources..."
     for p in ${BUILD_PLUGINS}; do
         CMAKE_OPTIONS="${CMAKE_OPTIONS} -DWITH_XC_$(echo $p | tr '[:lower:]' '[:upper:]')=On"
     done
-    
+    if [ "$(uname -o)" == "GNU/Linux" ] && ${build_appimage}; then
+        CMAKE_OPTIONS="${CMAKE_OPTIONS} -DKEEPASSXC_DIST_TYPE=AppImage"
+        # linuxdeploy requires /usr as install prefix
+        INSTALL_PREFIX="/usr"
+    fi
+
     if [ "$COMPILER" == "g++" ]; then
         export CC=gcc
     elif [ "$COMPILER" == "clang++" ]; then
         export CC=clang
     fi
     export CXX="$COMPILER"
-    
+
     if [ "" == "$DOCKER_IMAGE" ]; then
         if [ "$(uname -s)" == "Darwin" ]; then
             # Building on macOS
@@ -692,21 +910,27 @@ build() {
 
             logInfo "Compiling and packaging sources..."
             make ${MAKE_OPTIONS} package
-            
+
+            # Appsign the executables if desired
+            if [[ ${build_appsign} && ! -z ${build_key} ]]; then
+                logInfo "Signing executable files"
+                appsign "-f" "./${APP_NAME}-${RELEASE_NAME}.dmg" "-k" "${build_key}"
+            fi
+
             mv "./${APP_NAME}-${RELEASE_NAME}.dmg" ../
         elif [ "$(uname -o)" == "Msys" ]; then
             # Building on Windows with Msys2
             logInfo "Configuring build..."
             cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off -G"MSYS Makefiles" \
                 -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" ${CMAKE_OPTIONS} "$SRC_DIR"
-            
+
             logInfo "Compiling and packaging sources..."
             mingw32-make ${MAKE_OPTIONS} preinstall
 
             # Appsign the executables if desired
             if [[ ${build_appsign} && ! -z ${build_key} ]]; then
                 logInfo "Signing executable files"
-                appsign "-f" `find src | grep '\.exe'` "-k" "${build_key}"
+                appsign "-f" $(find src | grep '\.exe') "-k" "${build_key}"
             fi
 
             # Call cpack directly instead of calling make package.
@@ -717,47 +941,43 @@ build() {
             # Inject the portable config into the zip build and rename
             for filename in ${APP_NAME}-*.zip; do
                 logInfo "Creating portable zip file"
-                local folder=`echo ${filename} | sed -r 's/(.*)\.zip/\1/'`
+                local folder=$(echo ${filename} | sed -r 's/(.*)\.zip/\1/')
                 python -c 'import zipfile,sys ; zipfile.ZipFile(sys.argv[1],"a").write(sys.argv[2],sys.argv[3])' \
                     ${filename} ${SRC_DIR}/share/keepassxc.ini ${folder}/keepassxc.ini
                 mv ${filename} ${folder}-portable.zip
             done
-            
+
             mv "${APP_NAME}-"*.* ../
         else
-            mkdir -p "${OUTPUT_DIR}/bin-release"
-            
+            mkdir -p "${OUTPUT_DIR}/KeePassXC.AppDir"
+
             # Building on Linux without Docker container
             logInfo "Configuring build..."
             cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off ${CMAKE_OPTIONS} \
-                -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" \
-                -DKEEPASSXC_DIST_TYPE=AppImage "$SRC_DIR"
-                
+                -DCMAKE_INSTALL_PREFIX="${INSTALL_PREFIX}" "$SRC_DIR"
+
             logInfo "Compiling sources..."
-            make $MAKE_OPTIONS
-            
+            make ${MAKE_OPTIONS}
+
             logInfo "Installing to bin dir..."
-            make DESTDIR="${OUTPUT_DIR}/bin-release" install/strip
-            
-            logInfo "Creating AppImage..."
-            ${SRC_DIR}/AppImage-Recipe.sh "$APP_NAME" "$RELEASE_NAME"
+            make DESTDIR="${OUTPUT_DIR}/KeePassXC.AppDir" install/strip
         fi
     else
         if ${build_snapcraft}; then
             logInfo "Building snapcraft docker image..."
-            
+
             sudo docker image build -t "$DOCKER_IMAGE" "$(realpath "$SRC_DIR")/ci/snapcraft"
 
             logInfo "Launching Docker contain to compile snapcraft..."
-            
+
             sudo docker run --name "$DOCKER_CONTAINER_NAME" --rm \
                 -v "$(realpath "$SRC_DIR"):/keepassxc" -w "/keepassxc" \
-                "$DOCKER_IMAGE" snapcraft       
+                "$DOCKER_IMAGE" snapcraft
         else
-            mkdir -p "${OUTPUT_DIR}/bin-release"
-            
+            mkdir -p "${OUTPUT_DIR}/KeePassXC.AppDir"
+
             logInfo "Launching Docker container to compile sources..."
-            
+
             docker run --name "$DOCKER_CONTAINER_NAME" --rm \
                 --cap-add SYS_ADMIN --security-opt apparmor:unconfined --device /dev/fuse \
                 -e "CC=${CC}" -e "CXX=${CXX}" \
@@ -765,26 +985,40 @@ build() {
                 -v "$(realpath "$OUTPUT_DIR"):/keepassxc/out:rw" \
                 "$DOCKER_IMAGE" \
                 bash -c "cd /keepassxc/out/build-release && \
-                    cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off $CMAKE_OPTIONS \
-                        -DCMAKE_INSTALL_PREFIX=\"${INSTALL_PREFIX}\" \
-                        -DKEEPASSXC_DIST_TYPE=AppImage /keepassxc/src && \
-                    make $MAKE_OPTIONS && make DESTDIR=/keepassxc/out/bin-release install/strip && \
-                    /keepassxc/src/AppImage-Recipe.sh "$APP_NAME" "$RELEASE_NAME""
+                    cmake -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=Off ${CMAKE_OPTIONS} \
+                        -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} /keepassxc/src && \
+                    make ${MAKE_OPTIONS} && make DESTDIR=/keepassxc/out/KeePassXC.AppDir install/strip"
         fi
-        
+
         if [ 0 -ne $? ]; then
             exitError "Docker build failed!"
         fi
-        
+
         logInfo "Build finished, Docker container terminated."
     fi
-    
+
+    if [ "$(uname -o)" == "GNU/Linux" ] && ${build_appimage}; then
+        local appsign_flag=""
+        local appsign_key_flag=""
+        local docker_image_flag=""
+        local docker_container_name_flag=""
+        if ${build_appsign}; then
+            appsign_flag="--appsign"
+            appsign_key_flag="-k ${build_key}"
+        fi
+        if [ "" != "${DOCKER_IMAGE}" ]; then
+            docker_image_flag="-d ${DOCKER_IMAGE}"
+            docker_container_name_flag="--container-name ${DOCKER_CONTAINER_NAME}"
+        fi
+        appimage "-a" "${OUTPUT_DIR}/KeePassXC.AppDir" "-o" "${OUTPUT_DIR}" \
+            ${appsign_flag} ${appsign_key_flag} ${docker_image_flag} ${docker_container_name_flag}
+    fi
+
     cleanup
-    
+
     logInfo "All done!"
 }
 
-
 # -----------------------------------------------------------------------
 #                           gpgsign command
 # -----------------------------------------------------------------------
@@ -815,7 +1049,7 @@ gpgsign() {
         esac
         shift
     done
-    
+
     if [ -z "${sign_files}" ]; then
         logError "Missing arguments, --files is required!\n"
         printUsage "gpgsign"
@@ -829,7 +1063,7 @@ gpgsign() {
 
         logInfo "Signing file '${f}' using release key..."
         gpg --output "${f}.sig" --armor --local-user "$GPG_KEY" --detach-sig "$f"
-        
+
         if [ 0 -ne $? ]; then
             exitError "Signing failed!"
         fi
@@ -839,12 +1073,10 @@ gpgsign() {
         local bname="$(basename "$f")"
         (cd "$(dirname "$rp")"; sha256sum "$bname"  > "${bname}.DIGEST")
     done
-    
+
     logInfo "All done!"
 }
 
-
-
 # -----------------------------------------------------------------------
 #                           appsign command
 # -----------------------------------------------------------------------
@@ -971,7 +1203,7 @@ appsign() {
                 logInfo "Signing file '${f}' using Microsoft signtool..."
                 signtool sign -f "${key}" -p "${password}" -d "KeePassXC" \
                     -t "http://timestamp.comodoca.com/authenticode" "${f}"
-                
+
                 if [ 0 -ne $? ]; then
                     exitError "Signing failed!"
                 fi
@@ -987,7 +1219,6 @@ appsign() {
     logInfo "All done!"
 }
 
-
 # -----------------------------------------------------------------------
 #                       parse global command line
 # -----------------------------------------------------------------------
@@ -1000,7 +1231,8 @@ if [ "" == "$MODE" ]; then
 elif [ "help" == "$MODE" ]; then
     printUsage "$1"
     exit
-elif [ "check" == "$MODE" ] || [ "merge" == "$MODE" ] || [ "build" == "$MODE" ] || [ "gpgsign" == "$MODE" ] || [ "appsign" == "$MODE" ]; then
+elif [ "check" == "$MODE" ] || [ "merge" == "$MODE" ] || [ "build" == "$MODE" ] \
+    || [ "gpgsign" == "$MODE" ] || [ "appsign" == "$MODE" ] || [ "appimage" == "$MODE" ]; then
     ${MODE} "$@"
 else
     printUsage "$MODE"
diff --git a/src/gui/masterkey/YubiKeyEditWidget.cpp b/src/gui/masterkey/YubiKeyEditWidget.cpp
index 6991af427..bae3a3d1e 100644
--- a/src/gui/masterkey/YubiKeyEditWidget.cpp
+++ b/src/gui/masterkey/YubiKeyEditWidget.cpp
@@ -121,6 +121,9 @@ void YubiKeyEditWidget::yubikeyDetected(int slot, bool blocking)
     m_compUi->buttonRedetectYubikey->setEnabled(true);
     m_compUi->yubikeyProgress->setVisible(false);
     m_isDetected = true;
+#else
+    Q_UNUSED(slot);
+    Q_UNUSED(blocking);
 #endif
 }