diff --git a/_docs/dev_setup.md b/_docs/dev_setup.md
--- a/_docs/dev_setup.md
+++ b/_docs/dev_setup.md
@@ -70,6 +70,21 @@ the launcher, wait for the desktop window, make a Rust source edit, and check fo
 a rebuild/restart in the same terminal. Frontend edits should update the webview
 without restarting the Rust process.
 
+**Linux Installers (Ubuntu)**
+
+Build the `.deb` and AppImage on Ubuntu 22.04 or newer. `--install-deps` installs the
+WebKitGTK, GTK and wreq build dependencies with apt:
+
+```bash
+./script/artcraft/ubuntu_build_installers.sh --install-deps
+
+# Install it; ArtCraft then appears in the app launcher
+sudo apt install ./target/release/bundle/deb/ArtCraft_*_amd64.deb
+```
+
+The `.deb` installs `/usr/bin/artcraft`, a desktop entry and hicolor icons (32 to 512 px).
+CI builds the same packages with `.github/workflows/artcraft-linux-publish.yml`.
+
 **Windows Development**
 
 ```powershell
diff --git a/crates/desktop/artcraft/icons/256x256.png b/crates/desktop/artcraft/icons/256x256.png
new file mode 100644
index 0000000000000000000000000000000000000000..173f813f3624c436313f66d0089a54891541be40
GIT binary patch
literal 12535
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

literal 0
HcmV?d00001

diff --git a/crates/desktop/artcraft/linux/artcraft.desktop b/crates/desktop/artcraft/linux/artcraft.desktop
new file mode 100644
--- /dev/null
+++ b/crates/desktop/artcraft/linux/artcraft.desktop
@@ -0,0 +1,15 @@
+[Desktop Entry]
+Type=Application
+Name={{name}}
+GenericName=AI Image and Video Creator
+{{#if comment}}
+Comment={{comment}}
+{{/if}}
+Exec={{exec}}
+TryExec={{exec}}
+Icon={{icon}}
+Terminal=false
+StartupNotify=true
+StartupWMClass={{exec}}
+Categories={{categories}}
+Keywords=ai;image;video;generation;3d;art;design;film;
diff --git a/crates/desktop/artcraft/tauri.linux.conf.json b/crates/desktop/artcraft/tauri.linux.conf.json
new file mode 100644
--- /dev/null
+++ b/crates/desktop/artcraft/tauri.linux.conf.json
@@ -0,0 +1,26 @@
+{
+  "$schema": "https://schema.tauri.app/config/2.0.0-rc",
+  "bundle": {
+    "category": "GraphicsAndDesign",
+    "shortDescription": "Intentional crafting engine for artists, designers, and filmmakers",
+    "longDescription": "ArtCraft is an intentional crafting engine for artists, designers, and filmmakers. Generate and edit images, video, audio, 3D meshes and worlds.",
+    "publisher": "Learning Machines LLC",
+    "homepage": "https://getartcraft.com",
+    "icon": [
+      "icons/32x32.png",
+      "icons/64x64.png",
+      "icons/128x128.png",
+      "icons/256x256.png",
+      "icons/icon.png"
+    ],
+    "linux": {
+      "deb": {
+        "section": "graphics",
+        "desktopTemplate": "linux/artcraft.desktop"
+      },
+      "rpm": {
+        "desktopTemplate": "linux/artcraft.desktop"
+      }
+    }
+  }
+}
diff --git a/netlify.toml b/netlify.toml
new file mode 100644
--- /dev/null
+++ b/netlify.toml
@@ -0,0 +1,5 @@
+# The storyteller-docs Netlify site still builds this repository, but its
+# build command and the docs now live in storytold/artcraft-services, so every
+# deploy preview here fails. Skip those builds until the site is disconnected.
+[build]
+  ignore = "exit 0"
diff --git a/script/artcraft/ubuntu_build_installers.sh b/script/artcraft/ubuntu_build_installers.sh
new file mode 100755
--- /dev/null
+++ b/script/artcraft/ubuntu_build_installers.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+# Build the ArtCraft Linux installers (.deb and AppImage) on Ubuntu 22.04 or newer.
+#
+#   ./script/artcraft/ubuntu_build_installers.sh [--install-deps] [--bundles deb,appimage,rpm]
+#
+# Output: target/release/bundle/{deb,appimage,rpm}/
+# Install the .deb with: sudo apt install ./target/release/bundle/deb/ArtCraft_*_amd64.deb
+
+set -euo pipefail
+
+root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
+frontend_path="${root_dir}/frontend"
+rust_crate_path="${root_dir}/crates/desktop/artcraft"
+
+install_deps=0
+bundles="deb,appimage"
+while [ $# -gt 0 ]; do
+  case "$1" in
+    --install-deps) install_deps=1; shift ;;
+    --bundles) bundles="$2"; shift 2 ;;
+    -h | --help) sed -n '2,7p' "$0"; exit 0 ;;
+    *) echo "unknown argument: $1" >&2; exit 2 ;;
+  esac
+done
+
+if [ "${install_deps}" = 1 ]; then
+  # Tauri needs WebKitGTK 4.1 and GTK 3; wreq (BoringSSL) needs cmake, perl, nasm and clang.
+  # bindgen (via boring-sys2/wreq) needs libclang-dev: clang alone doesn't ship the
+  # libclang.so that clang-sys loads.
+  sudo apt-get update
+  sudo apt-get install -y build-essential clang cmake file libayatana-appindicator3-dev \
+    libclang-dev libgtk-3-dev librsvg2-dev libssl-dev libwebkit2gtk-4.1-dev libxdo-dev nasm \
+    patchelf perl
+fi
+
+if ! cargo tauri --version >/dev/null 2>&1; then
+  echo 'ERROR: Install the Tauri CLI: cargo install tauri-cli --version "^2" --locked' >&2
+  exit 1
+fi
+
+pushd "${frontend_path}" >/dev/null
+npm ci
+NODE_OPTIONS="--max-old-space-size=8192" npx nx run artcraft:build
+popd >/dev/null
+
+pushd "${rust_crate_path}" >/dev/null
+# The frontend is already built, so skip Tauri's beforeBuildCommand. AppImage tooling runs
+# without FUSE when extracted.
+SQLX_OFFLINE=true APPIMAGE_EXTRACT_AND_RUN=1 \
+  cargo tauri build --bundles "${bundles}" --config '{"build":{"beforeBuildCommand":""}}'
+popd >/dev/null
+
+ls -lh "${root_dir}"/target/release/bundle/*/ArtCraft*
