diff --git a/.github/packaging/linux/AppDir/AppRun b/.github/packaging/linux/AppDir/AppRun
new file mode 100644
index 00000000..b047e32a
--- /dev/null
+++ b/.github/packaging/linux/AppDir/AppRun
@@ -0,0 +1,81 @@
+#!/bin/sh
+LAUNCH_DIR="$PWD"
+
+# Define persistent storage for extracted LNbits
+PERSISTENT_DIR="$HOME/.local/share/lnbits"
+
+# Remove existing LNbits directory before extraction
+if [ -d "$PERSISTENT_DIR" ]; then
+ echo "Removing existing LNbits directory..."
+ rm -rf "$PERSISTENT_DIR"
+fi
+
+# Ensure the persistent directory exists
+mkdir -p "$PERSISTENT_DIR"
+
+# Extract LNbits from the AppImage if not already extracted
+echo "Extracting LNbits to disk for better performance..."
+cp -r "$APPDIR/usr/lnbits"/* "$PERSISTENT_DIR/"
+chmod +x "$PERSISTENT_DIR/dist/lnbits"
+
+# Check if the directory exists, and create it if it doesn't
+if [ ! -d "$LAUNCH_DIR/lnbits/database" ]; then
+ mkdir -p "$LAUNCH_DIR/lnbits/database"
+ echo "Created database directory at $LAUNCH_DIR/lnbits/database"
+fi
+
+if [ ! -d "$LAUNCH_DIR/lnbits/extensions" ]; then
+ mkdir -p "$LAUNCH_DIR/lnbits/extensions"
+ echo "Created extensions directory at $LAUNCH_DIR/lnbits/extensions"
+fi
+
+cd "$PERSISTENT_DIR"
+
+# Export the directory as an environment variable for the app
+LNBITS_DATA_FOLDER="${LNBITS_DATA_FOLDER:-$LAUNCH_DIR/lnbits/database}"
+LNBITS_EXTENSIONS_PATH="${LNBITS_EXTENSIONS_PATH:-$LAUNCH_DIR/lnbits/extensions}"
+export LNBITS_DATA_FOLDER
+export LNBITS_EXTENSIONS_PATH
+export LNBITS_ADMIN_UI=true
+
+# Define the LNbits URL
+URL="http://0.0.0.0:5000"
+
+"./dist/lnbits" "$@" &
+LNBITS_PID=$!
+
+# Wait for LNbits to be ready before showing the popup
+sleep 3
+CLOSED=false
+
+# Function to stop LNbits gracefully
+kill_lnbits() {
+ LN_PIDS=$(lsof -t -i:5000 2>/dev/null) # Capture all PIDs
+ if [ -n "$LN_PIDS" ]; then
+ echo "Stopping LNbits (PIDs: $LN_PIDS)..."
+ kill -2 $LN_PIDS # Send SIGINT to all processes on port 5000
+ CLOSED=true
+ fi
+}
+
+# Show a GUI with a clickable link to open the browser
+if command -v zenity >/dev/null 2>&1; then
+ while [ "$CLOSED" = false ]; do
+ zenity --info --title="LNbits" --width=400 --text="LNbits is running.\n\n$URL\n\nClick 'Close Server' to stop LNbits." --ok-label="Close Server"
+ kill_lnbits
+ sleep 1
+ done
+elif command -v yad >/dev/null 2>&1; then
+ while [ "$CLOSED" = false ]; do
+ yad --title="LNbits" --width=400 --text="LNbits is running.\n\n$URL\n\nClick 'Close Server' to stop LNbits." --button="Close Server":0
+ kill_lnbits
+ sleep 1
+ done
+else
+ echo "No GUI tool found. LNbits is running at $URL"
+fi
+
+# Ensure the script doesn't hang after closing
+if ps -p $LNBITS_PID >/dev/null 2>&1; then
+ wait $LNBITS_PID 2>/dev/null || true
+fi
diff --git a/.github/packaging/linux/AppDir/lnbits.desktop b/.github/packaging/linux/AppDir/lnbits.desktop
new file mode 100644
index 00000000..2946c212
--- /dev/null
+++ b/.github/packaging/linux/AppDir/lnbits.desktop
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Name=LNbits
+Exec=lnbits
+Icon=lnbits
+Type=Application
+Categories=X-Bitcoin;X-LightningNetwork;X-Finance;Network;
diff --git a/.github/packaging/linux/AppDir/lnbits.png b/.github/packaging/linux/AppDir/lnbits.png
new file mode 100644
index 00000000..5437551b
Binary files /dev/null and b/.github/packaging/linux/AppDir/lnbits.png differ
diff --git a/.github/packaging/linux/AppDir/usr/share/icons/128x128/apps/lnbits.png b/.github/packaging/linux/AppDir/usr/share/icons/128x128/apps/lnbits.png
new file mode 100644
index 00000000..2dc26564
Binary files /dev/null and b/.github/packaging/linux/AppDir/usr/share/icons/128x128/apps/lnbits.png differ
diff --git a/.github/packaging/linux/AppDir/usr/share/icons/256x256/apps/lnbits.png b/.github/packaging/linux/AppDir/usr/share/icons/256x256/apps/lnbits.png
new file mode 100644
index 00000000..2eb6d2d7
Binary files /dev/null and b/.github/packaging/linux/AppDir/usr/share/icons/256x256/apps/lnbits.png differ
diff --git a/.github/packaging/linux/AppDir/usr/share/icons/512x512/apps/lnbits.png b/.github/packaging/linux/AppDir/usr/share/icons/512x512/apps/lnbits.png
new file mode 100644
index 00000000..38a15238
Binary files /dev/null and b/.github/packaging/linux/AppDir/usr/share/icons/512x512/apps/lnbits.png differ
diff --git a/.github/workflows/packaging.yml b/.github/workflows/packaging.yml
new file mode 100644
index 00000000..0fa8dee9
--- /dev/null
+++ b/.github/workflows/packaging.yml
@@ -0,0 +1,73 @@
+name: Build LNbits AppImage DMG
+
+on:
+ release:
+ types: [published]
+
+jobs:
+
+ build-linux-package:
+ runs-on: ubuntu-latest
+ steps:
+ # Step 1: Checkout the repository
+ - name: Checkout code
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ # Step 2: Install Dependencies
+ - name: Install Dependencies
+ run: |
+ curl -sSL https://install.python-poetry.org | python3 -
+ echo "$HOME/.local/bin" >> $GITHUB_PATH
+ sudo apt-get update
+ sudo apt-get install -y libfuse2
+ shell: bash
+
+ # Step 3: Clone LNbits Repository
+ - name: Clone LNbits
+ run: |
+ mv .github/packaging packaging
+ mkdir -p packaging/linux/AppDir/usr
+ git clone https://github.com/lnbits/lnbits.git packaging/linux/AppDir/usr/lnbits
+ shell: bash
+
+ # Step 4: Make the AppImage Asset
+ - name: Make Asset
+ run: |
+ cd packaging/linux/AppDir/usr/lnbits
+ git checkout main
+ poetry install --main
+ poetry run pip install pyinstaller
+
+ # Build the LNbits binary
+ poetry run pyinstaller --onefile --name lnbits --collect-all lnbits --collect-all sqlalchemy --collect-all aiosqlite --hidden-import=passlib.handlers.bcrypt $(poetry run which lnbits)
+ cd ../../../../..
+ chmod +x packaging/linux/AppDir/AppRun
+ chmod +x packaging/linux/AppDir/lnbits.desktop
+ chmod +x packaging/linux/AppDir/usr/lnbits/dist/lnbits
+
+ find packaging/linux/AppDir/usr/lnbits -mindepth 1 -maxdepth 1 \
+ ! -name 'dist' \
+ ! -name 'lnbits' \
+ -exec rm -rf {} +
+
+ wget https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
+ chmod +x appimagetool-x86_64.AppImage
+ TAG_NAME=${{ github.event.release.tag_name }}
+ APPIMAGE_NAME="LNbits-${TAG_NAME}.AppImage"
+ ./appimagetool-x86_64.AppImage --updateinformation "gh-releases-zsync|lnbits|lnbits|latest|*.AppImage.zsync" packaging/linux/AppDir "$APPIMAGE_NAME"
+ chmod +x "$APPIMAGE_NAME"
+ echo "APPIMAGE_NAME=$APPIMAGE_NAME" >> $GITHUB_ENV
+ shell: bash
+
+ # Step 5: Upload Linux Release Asset
+ - name: Upload Linux Release Asset
+ uses: actions/upload-release-asset@v1
+ with:
+ upload_url: ${{ github.event.release.upload_url }}
+ asset_path: ${{ env.APPIMAGE_NAME }}
+ asset_name: ${{ env.APPIMAGE_NAME }}
+ asset_content_type: application/octet-stream
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index db763bd6..d1f0f874 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -6,11 +6,23 @@ nav_order: 2
# Basic installation
-The following sections explain how to install LNbits using varions package managers: `poetry`, `nix`, `Docker` and `Fly.io`.
-
Note that by default LNbits uses SQLite as its database, which is simple and effective but you can configure it to use PostgreSQL instead which is also described in a section below.
-## Option 1 (recommended): Poetry
+## Option 1: AppImage (LInux)
+
+### AppImage (Linux)
+
+Go to [releases](https://github.com/lnbits/lnbits/releases) and pull latest AppImage, or:
+
+```sh
+wget $(curl -s https://api.github.com/repos/lnbits/lnbits/releases/latest | jq -r '.assets[] | select(.name | endswith(".AppImage")) | .browser_download_url') -O LNbits-latest.AppImage
+chmod +x LNbits-latest.AppImage
+./LNbits-latest.AppImage --host 0.0.0.0
+```
+
+LNbits will create a folder for db and extension files in the folder the AppImage runs from.
+
+## Option 2: Poetry (recommended for developers)
It is recommended to use the latest version of Poetry. Make sure you have Python version `3.12` installed.
@@ -71,18 +83,6 @@ poetry install --only main
# Start LNbits with `poetry run lnbits`
```
-## Option 2: Install script (on Debian/Ubuntu)
-
-```sh
-wget https://raw.githubusercontent.com/lnbits/lnbits/main/lnbits.sh &&
-chmod +x lnbits.sh &&
-./lnbits.sh
-```
-
-Now visit `0.0.0.0:5000` to make a super-user account.
-
-`export PATH="/home/$USER/.local/bin:$PATH"` then `./lnbits.sh` can be used to run, but for more control `cd lnbits` and use `poetry run lnbits` (see previous option).
-
## Option 3: Nix
```sh
diff --git a/lnbits.sh b/lnbits.sh
deleted file mode 100644
index 38518fba..00000000
--- a/lnbits.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/bash
-
-# Check install has not already run
-if [ ! -d lnbits/data ]; then
-
- # Update package list and install prerequisites non-interactively
- sudo apt update -y
- sudo apt install -y software-properties-common
-
- # Add the deadsnakes PPA repository non-interactively
- sudo add-apt-repository -y ppa:deadsnakes/ppa
-
- # Install Python 3.9 and distutils non-interactively
- sudo apt install -y python3.9 python3.9-distutils
-
- # Install Poetry
- curl -sSL https://install.python-poetry.org | python3.9 -
-
- # Add Poetry to PATH for the current session
- export PATH="/home/$USER/.local/bin:$PATH"
-
- if [ ! -d lnbits/wallets ]; then
- # Clone the LNbits repository
- git clone https://github.com/lnbits/lnbits.git
- if [ $? -ne 0 ]; then
- echo "Failed to clone the repository ... FAIL"
- exit 1
- fi
- # Ensure we are in the lnbits directory
- cd lnbits || { echo "Failed to cd into lnbits ... FAIL"; exit 1; }
- fi
-
- git checkout main
- # Make data folder
- mkdir data
-
- # Copy the .env.example to .env
- cp .env.example .env
-
-elif [ ! -d lnbits/wallets ]; then
- # cd into lnbits
- cd lnbits || { echo "Failed to cd into lnbits ... FAIL"; exit 1; }
-fi
-
-# Set path for running after install
-export PATH="/home/$USER/.local/bin:$PATH"
-
-# Install the dependencies using Poetry
-poetry env use python3.9
-poetry install --only main
-
-# Set environment variables for LNbits
-export LNBITS_ADMIN_UI=true
-export HOST=0.0.0.0
-
-# Run LNbits
-poetry run lnbits
\ No newline at end of file