diff --git a/android/universal_apk.gitlab-ci.yml b/android/universal_apk.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..dc475208e85c4ac04bce8a804c1ccae6c09d0e3c
--- /dev/null
+++ b/android/universal_apk.gitlab-ci.yml
@@ -0,0 +1,33 @@
+# Required variables:
+## SIGNING_KEYSTORE: The keystore, base64 encoded
+## SIGNING_STORE_PW: The password for the keystore, keyPassword property in the signing.properties file
+## SIGNING_KEY_PW: The password for the key, keyPassword property in the signing.properties file
+## SIGNING_KEY_ALIAS: The alias for the key, keyAlias property in the signing.properties file
+## APPLICATION_VERSION: The build name of the application
+## APPLICATION_BUILD_NUMBER: The build number of the application
+## AAB_NAME: Path to the built aab file
+## APK_NAME: The name of the output apk
+
+stages:
+- build
+
+variables:
+  BUNDLETOOL_IMAGE: andrijoos/bundletool
+  APK_NAME: ${APP_NAME}_${APPLICATION_VERSION}.apk
+
+.build_universal_apk:
+  stage: build
+  image: 
+    name: ${BUNDLETOOL_IMAGE}
+    pull_policy: always
+  tags:
+    - amd64
+  artifacts:
+    paths:
+      - ${APK_NAME}
+    expire_in: 1 day
+  script:
+    - echo $SIGNING_KEYSTORE | base64 -d > keystore.jks
+    - bundletool build-apks --bundle=${AAB_NAME} --output=apks.apks --mode=universal --ks=keystore.jks --ks-pass=pass:$SIGNING_STORE_PW --ks-key-alias=$SIGNING_KEY_ALIAS --key-pass=pass:$SIGNING_KEY_PW
+    - unzip -q apks.apks -d apks
+    - mv apks/universal.apk ./${APK_NAME}
diff --git a/flutter/flutter-android.gitlab-ci.yml b/flutter/flutter-android.gitlab-ci.yml
index 8f5dd111924953f37cdf376632b4f4a98c1274bc..7a673bfc93e043e67c71c3d3382897c736bc0df7 100644
--- a/flutter/flutter-android.gitlab-ci.yml
+++ b/flutter/flutter-android.gitlab-ci.yml
@@ -5,7 +5,7 @@
 ## SIGNING_KEY_PW: The password for the key, keyPassword property in the signing.properties file
 ## SIGNING_KEY_ALIAS: The alias for the key, keyAlias property in the signing.properties file
 ## APP_DIRECTORY: The directory of the app
-## APP_NAME: The name of the output aab, the resulting apk will be named ${APP_NAME}_${APPLICATION_VERSION}.apk
+## AAB_NAME: The name of the output aab
 ## APPLICATION_VERSION: The build name of the application
 ## APPLICATION_BUILD_NUMBER: The build number of the application
 
@@ -16,10 +16,7 @@ variables:
   SIGNING_PROPERTIES_FILE: android/signing.properties
   SINGING_KEYSTORE_FILE: android/signing-keystore.jks
   FLUTTER_ANDROID_IMAGE: andrijoos/flutter:3.24.5-android
-  BUNDLETOOL_IMAGE: andrijoos/bundletool
   ANDROID_PLATFORMS: android-arm,android-arm64,android-x64
-  AAB_NAME: ${APP_NAME}_${APPLICATION_VERSION}.aab
-  APK_NAME: ${APP_NAME}_${APPLICATION_VERSION}.apk
 
 .build_android:
   stage: build
@@ -42,20 +39,3 @@ variables:
     - flutter build appbundle --release --target-platform ${ANDROID_PLATFORMS} --build-name ${APPLICATION_VERSION} --build-number ${APPLICATION_BUILD_NUMBER}
   after_script:
     - mv ${APP_DIRECTORY}/build/app/outputs/bundle/release/app-release.aab ./${AAB_NAME}
-
-.build_universal_apk:
-  stage: build
-  image: 
-    name: ${BUNDLETOOL_IMAGE}
-    pull_policy: always
-  tags:
-    - amd64
-  artifacts:
-    paths:
-      - ${APK_NAME}
-    expire_in: 1 day
-  script:
-    - echo $SIGNING_KEYSTORE | base64 -d > keystore.jks
-    - bundletool build-apks --bundle=${AAB_NAME} --output=apks.apks --mode=universal --ks=keystore.jks --ks-pass=pass:$SIGNING_STORE_PW --ks-key-alias=$SIGNING_KEY_ALIAS --key-pass=pass:$SIGNING_KEY_PW
-    - unzip -q apks.apks -d apks
-    - mv apks/universal.apk ./${APK_NAME}
diff --git a/flutter/flutter-linux.gitlab-ci.yml b/flutter/flutter-linux.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c0406f2bc290f06d490b1f5091b5842a1abb0b77
--- /dev/null
+++ b/flutter/flutter-linux.gitlab-ci.yml
@@ -0,0 +1,39 @@
+# Required variables:
+## APP_DIRECTORY: The directory of the app
+## APPLICATION_VERSION: The build name of the application
+## APPLICATION_BUILD_NUMBER: The build number of the application
+## LINUX_BIN_NAME: The name of the output bin
+
+stages:
+- build
+
+variables:
+  FLUTTER_LINUX_IMAGE: andrijoos/flutter:3.24.5-linux
+  APPIMAGE_BUILDER_IMAGE: appimagecrafters/appimage-builder:1.1.0
+
+  LINUX_PLATFORMS: x64 # linux-arm64 not available yet, as cross-compilation not supported
+
+.build_linux:
+  stage: build
+  image: ${FLUTTER_LINUX_IMAGE}
+  tags:
+  - amd64
+  artifacts:
+    paths:
+    - x64
+    expire_in: 1 day
+  script:
+  - cd ${APP_DIRECTORY}
+  - |
+    IFS=',' read -r -a platforms <<< "${LINUX_PLATFORMS}"
+    for platform in "${platforms[@]}"; do
+      flutter build linux --release --target-platform linux-${platform} --build-name ${APPLICATION_VERSION} --build-number ${APPLICATION_BUILD_NUMBER}
+    done
+  after_script:
+  - |
+    IFS=',' read -r -a platforms <<< "${LINUX_PLATFORMS}"
+    for platform in "${platforms[@]}"; do
+      mkdir -p ${platform}
+      mv ${APP_DIRECTORY}/build/linux/x64/release/bundle/* ./${platform}
+      mv ${platform}/${APP_NAME} ${platform}/${LINUX_BIN_NAME}
+    done
diff --git a/linux/appimage.gitlab-ci.yml b/linux/appimage.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..077b4d13aadc7f6de64aa108823bdf868a26592b
--- /dev/null
+++ b/linux/appimage.gitlab-ci.yml
@@ -0,0 +1,81 @@
+# Required variables:
+## APP_NAME
+## APP_DIR
+## APPIMAGE_VERSION
+## APPIMAGE_BUILDER_CONFIG
+## APPIMAGE_EXECUTABLE_NAME
+## APPIMAGE_ICON
+## APPIMAGE_ICON_DIMENSIONS
+## APPIMAGE_NAME
+
+stages:
+- build
+
+variables:
+  APPIMAGE_BUILDER_IMAGE: appimagecrafters/appimage-builder:1.1.0
+  APPIMAGE_PLATFORM: x86_64
+
+  APPIMAGE_SIGN_KEY: None
+  APPIMAGE_SIGN: false
+
+.build_appimage:
+  stage: build
+  image: ${APPIMAGE_BUILDER_IMAGE}
+  tags:
+  - amd64
+  artifacts:
+    paths:
+    - "*.AppImage"
+    - "*.AppImage.zsync"
+    - "*.asc"
+    expire_in: 1 day
+  variables:
+    APPDIR_SUBDIR: AppDir
+  script:
+  - which mksquashfs || apt install squashfs-tools # appimagecrafters/appimage-builder currently doesn't provide mksquashfs https://github.com/AppImageCrafters/appimage-builder/issues/271
+  - |
+    if [ "${APPIMAGE_SIGN}" == "true" ]
+    then
+      echo "${APPIMAGE_SIGN_CERTIFICATE}" | base64 -d | gpg2 --batch --import
+    fi
+  - |
+    APPDIR_PATH=$PWD/${APPDIR_SUBDIR}
+    ICON=$(basename "${APPIMAGE_ICON}")
+    ICON_DIR="${APPDIR_SUBDIR}/usr/share/icons/hicolor/${APPIMAGE_ICON_DIMENSIONS}/apps/"
+
+    sed -i "s|\${APPDIR_PATH}|${APPDIR_PATH}|" ${APPIMAGE_BUILDER_CONFIG}
+    sed -i "s/\${APP_NAME}/${APP_NAME}/" ${APPIMAGE_BUILDER_CONFIG}
+    sed -i "s/\${VERSION}/${APPIMAGE_VERSION}/" ${APPIMAGE_BUILDER_CONFIG}
+    sed -i "s/\${ICON}/${ICON}/" ${APPIMAGE_BUILDER_CONFIG}
+    sed -i "s/\${EXECUTABLE}/${APPIMAGE_EXECUTABLE_NAME}/" ${APPIMAGE_BUILDER_CONFIG}
+    sed -i "s/\${SIGN_KEY}/\"\"/" ${APPIMAGE_BUILDER_CONFIG}
+    sed -i "s/\${ARCH}/${APPIMAGE_PLATFORM}/" ${APPIMAGE_BUILDER_CONFIG}
+
+    if [ "${APPIMAGE_PLATFORM}" == "x86_64" ]
+    then
+      APT_ARCH="amd64"
+    elif [ "${APPIMAGE_PLATFORM}" == "arm64" ]
+    then
+      APT_ARCH="arm64"
+    fi
+
+    BUILDER_CONFIG="builder_config.yml"
+    cp ${APPIMAGE_BUILDER_CONFIG} ${BUILDER_CONFIG}
+    sed -i "s/\${APT_ARCH}/${APT_ARCH}/" ${BUILDER_CONFIG}
+
+    mkdir -p ${APPDIR_SUBDIR}
+    mv ${APP_DIR}/* ${APPDIR_SUBDIR}
+
+    mkdir -p ${ICON_DIR}
+    cp ${APPIMAGE_ICON} ${ICON_DIR}
+
+    appimage-builder --skip-tests --recipe ${BUILDER_CONFIG}
+
+    GENERATED_APPIMAGE_NAME="${APP_NAME}-${APPIMAGE_VERSION}-${APPIMAGE_PLATFORM}"
+    mv ${GENERATED_APPIMAGE_NAME}.AppImage ${APPIMAGE_NAME}.AppImage
+    mv ${GENERATED_APPIMAGE_NAME}.AppImage.zsync ${APPIMAGE_NAME}.AppImage.zsync
+
+    if [ "${APPIMAGE_SIGN}" == "true" ]
+    then
+      gpg2 --pinentry-mode loopback --passphrase ${APPIMAGE_SIGN_PASSWORD} --sign --detach-sig --armor --default-key ${APPIMAGE_SIGN_KEY} ${APPIMAGE_NAME}.AppImage
+    fi
diff --git a/linux/snap.gitlab-ci.yml b/linux/snap.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9b7f3509dcc25afb97bab0fbd94bd8f5c1516fa5
--- /dev/null
+++ b/linux/snap.gitlab-ci.yml
@@ -0,0 +1,53 @@
+# Required variables:
+## APP_NAME
+## APP_DIR
+## APP_VERSION
+## APP_SUMMARY
+## APP_DESCRIPTION
+## SNAP_BASE
+## SNAP_GRADE
+## SNAP_COMMAND
+## SNAP_PLATFORM
+## SNAP_NAME
+## SNAP_CONFIG
+
+stages:
+- build
+
+variables:
+  SNAPCRAFT_IMAGE: ghcr.io/canonical/snapcraft:8_core24
+
+  SNAP_CONFINMENT: strict
+
+.build_snap:
+  stage: build
+  image: ${SNAPCRAFT_IMAGE}
+  tags:
+  - amd64
+  artifacts:
+    paths:
+    - "*.snap"
+    expire_in: 1 day
+  variables:
+    BUILD_DIR: build
+  script:
+  - |
+    sed -i "s/\${APP_NAME}/${APP_NAME}/" ${SNAP_CONFIG}
+    sed -i "s/\${VERSION}/${APP_VERSION}/" ${SNAP_CONFIG}
+    sed -i "s/\${SUMMARY}/${APP_SUMMARY}/" ${SNAP_CONFIG}
+    sed -i "s/\${DESCRIPTION}/${APP_DESCRIPTION}/" ${SNAP_CONFIG}
+    sed -i "s/\${CONFINEMENT}/${SNAP_CONFINMENT}/" ${SNAP_CONFIG}
+    sed -i "s/\${BASE}/${SNAP_BASE}/" ${SNAP_CONFIG}
+    sed -i "s/\${GRADE}/${SNAP_GRADE}/" ${SNAP_CONFIG}
+    sed -i "s/\${COMMAND}/${SNAP_COMMAND}/" ${SNAP_CONFIG}
+
+    mkdir -p ${BUILD_DIR}/meta
+    cp -r ${APP_DIR}/* ${BUILD_DIR}
+    cp ${SNAP_CONFIG} ${BUILD_DIR}/meta/snap.yaml
+
+    snapcraft pack --platform ${SNAP_PLATFORM} ${BUILD_DIR}
+    rm -r ${BUILD_DIR}
+
+    GENERATED_SNAP="${APP_NAME}_${APP_VERSION}_all.snap"
+    ls .
+    mv "${GENERATED_SNAP}" "${SNAP_NAME}.snap"