Centralised file backup qube (Cryptomator)

I backup my templates and AppVMs occasionally via the Qubes backup tool but I would like to automate a more frequent cloud backup solution for some of my important files within my AppVMs. What I’m thinking is that I will create a centralised qube with access to cloud storage via Cryptomator. And I will somehow copy my important files from their respective AppVMs to the “Cryptomator” AppVM which will then back them up to cloud storage. This compartmentalises Cryptomator and the cloud storage apps to one place.

I’m wondering if there are any suggestions on the best way to get these files from their respective AppVM to the “Cryptomator” AppVM. I’d rather not punch holes in firewalls and have the qubes communicating directly via tcp so I’m thinking the most secure way would be to make use of qvm-copy, perhaps staging via ~/QubesIncoming and then onto the cloud storage from there. But it doesn’t seem very efficient.

Is there a better way? What about some kind of functionality similar to sharing a USB device from sys-usb to AppVM that might allow me to share a mount point from cryptomator-vm to AppVM?

Replying to self but I found a similar discussion here so I think the solution will look similar.

It is still not 100% ready but maybe this could guide you in coding it yourself.

@Glimmer868 beside the mounting approach, this is imo the most simple option.

qvm-run --pass-io SourceQube "tar cf - /home/user/foobar/" | qvm-run --pass-io DestQube "tar xvf - -C /"
qvm-run --pass-io SourceQube "cat /home/user/file.txt" | qvm-run --pass-io DestQube "cat > /home/user/file.txt"

Edit: Corrected 2nd option (thanks @solene). :handshake:

2 Likes

a cat is missing in the destqube command, should be “cat > /home/user/file.txt” and >/dev/null is too much

qvm-run --pass-io SourceQube "cat /home/user/file.txt" | qvm-run --pass-io DestQube "cat > /home/user/destination.txt"
1 Like

Alternative approaches would be to use rsync or syncthing over qrexec -
syncthing would provide a completely automated solution, which seems to
match your requirements.
I salt syncthing here with the packaged version
here. That provides a syncthing
server to access external nodes, and a syncthing qubes service that you
can enable between qubes.
If you only have a few files then a scripted qvm-copy would do - if you
have more than a few, and they change over time, then an automated
system would be better.
If you want advice on setting up one of these services just ask.

I never presume to speak for the Qubes team. When I comment in the Forum I speak for myself.
1 Like

Thank you @whoami and @solene - this seems like a lightweight solution that might suit my needs perfectly well.

Thanks @unman - you’ve given me plenty of topics to research there as I’m not familiar with any of them. I think I’m going to put in place the piped qvm-run solution immediately for a few essential files and then read up on your suggestions as a learning opportunity if nothing else.

Have you considered exploring wyng and wyng-util-qubes?

Thanks - I haven’t although this particular backup requirement is to backup certain user files to cloud storage in a manner which allows them to be retrieved and used with very few tools. I’m thinking in cases where my PC blows up and I have to use another device until I can replace it. The other solutions seem to meet those needs for now though.

I’ve found the below, via rsync, rather painless and perfectly granular for those kinds of backups. It also uses salt as @unman suggested :

I'm also thinking about this topic "centralised file backup qube" and trying to figure out a user-friendly setup.

Workflow concept for secure cloud backup:

Source AppVMs → Vault AppVM → Cloud AppVM → MEGA Cloud
     ↓              ↓              ↓              ↓
  [Files]     [Encryption and  [Network    [Cloud Storage]
                Air-Gapped]      Sync]

Setup features:

  • User-Friendly: GUI tools and no salt for the god sake. My instinct tells me to use gocryptfs and megacmd or rclone with integration scripts. Yes I think in a sophisticated and elagant setup with Nextcloud.
  • Compartmentalization: separation between vault and cloud operations.
  • Air-Gapped Encryption in Vault: before leaving the secure vault.
  • Client-Side Encryption: MEGA only receives already-encrypted vault files.

PROTOTYPE

Necessary fixes or improvements

  • Corretly and secure handle the user space (appvm) and the root space (template)
  • User space (appvm) and the root space (template) are breaking my mind
  • Restric the RPC policies and maybe create variables at the top of the script for user choice
  • Restric the user-bind
  • Long tests with daily tasks

qubes-gui-cloud-backup.sh

#!/bin/bash

################################################################################
# File Name   : qubes-gui-cloud-backup.sh
# Description : User-friendly encrypted cloud backup with Cryptomator GUI and
#               MEGA GUI. Provides seamless integration between air-gapped
#               vault management and cloud synchronization.
# Usage       : • Transfer to dom0:
#                qvm-run -p appvm 'cat ~/qubes-gui-cloud-backup.sh' > ~/qubes-gui-cloud-backup.sh
#               • Make executable:
#               chmod +x ~/qubes-gui-cloud-backup.sh
#               • Run:
#               bash ~/qubes-gui-cloud-backup.sh
# Author      : Me and thebois
# License     : Free of charge, no warranty
# Last edited : 2025-11-11
################################################################################

# Safety first
set -euo pipefail

# Configuration
BASE_TEMPLATE="debian-12-minimal"
VAULT_TEMPLATE="debian-vault-template"
CLOUD_TEMPLATE="debian-cloud-template"
VAULT_APPVM="appvm-vault"
CLOUD_APPVM="appvm-cloud"
CLOUD_APPVM_NETVM="sys-whonix"
PRIV_VOL_SIZE="15000000000" # 15GB for vaults
CRYPTOMATOR_URL="https://api.github.com/repos/cryptomator/cryptomator/releases/latest"
MEGA_BASE_URL="https://mega.nz/linux/repo/Debian_12/"
MEGA_VER_URL="https://mega.nz/linux/repo/Debian_12/Packages"

# Step 1: Verify and create base template
create_base_template() {
  echo -e "\nStep 1: Verifying and creating: $BASE_TEMPLATE..."

  if ! qvm-check "$BASE_TEMPLATE"; then
    echo -e "\nInstalling $BASE_TEMPLATE..."
    sudo qubes-dom0-update "qubes-template-$BASE_TEMPLATE"
  fi

  # Ensure template is shut down
  qvm-shutdown --wait "$BASE_TEMPLATE" 2>/dev/null || true

  if qvm-check "$BASE_TEMPLATE"; then
    echo -e "\nUpdating $BASE_TEMPLATE..."
    sudo qubesctl --show-output --skip-dom0 --targets="$BASE_TEMPLATE" state.sls update.qubes-vm
  fi

  echo -e "\nBase template setup complete"
}

# Configure bind-dirs for persistence
configure_bind_dirs() {
  local template="$1"
  local app_name="$2"

  echo "Configuring bind-dirs for $app_name in $template..."

  qvm-run -p -u root "$template" "
  # Create bind-dirs configuration directory
  mkdir -p /rw/config/qubes-bind-dirs.d

  # Create configuration file
  cat > /rw/config/qubes-bind-dirs.d/50_${app_name}.conf </dev/null; then
    echo "Creating Vault Template..."
    qvm-clone "$BASE_TEMPLATE" "$VAULT_TEMPLATE"
    qvm-prefs "$VAULT_TEMPLATE" label black
  fi

  qvm-start --skip-if-running "$VAULT_TEMPLATE"

  # Configure bind-dirs first
  configure_bind_dirs "$VAULT_TEMPLATE" "cryptomator"

  # Set environment
  qvm-run -p -u root "$VAULT_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
  qvm-run -p -u root "$VAULT_TEMPLATE" "sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen"
  qvm-run -p -u root "$VAULT_TEMPLATE" "locale-gen en_US.UTF-8"

  # Install dependencies IN TEMPLATE (persistent)
  qvm-run -p -u root "$VAULT_TEMPLATE" "
  apt-get install -y --no-install-recommends \
  qubes-core-agent-networking \
  qubes-core-agent-passwordless-root \
  curl \
  fuse3 \
  libasound2 \
  libxtst6 \
  thunar \
  qubes-core-agent-thunar \
  mousepad \
  less \
  psmisc \
  xfce4-terminal
"

# Create mimeapps.list with default MIME type associations
  local temp_mime_file=$(mktemp)
  cat > "$temp_mime_file" < "$temp_crypto_script" <&2
  exit 1
fi

# Extract the correct x86_64 AppImage URL
VERSION_URL=$(echo "$RESPONSE" | grep -o '"browser_download_url": *"[^"]*"' | grep "x86_64.*AppImage" | cut -d '"' -f 4 | head -n 1)

# Show what URL was found
echo "Found Cryptomator URL: $VERSION_URL"

# If no URL was found, exit with error
if [[ -z "$VERSION_URL" ]]; then
  echo "ERROR: No valid x86_64 AppImage URL found for $APP_NAME." >&2
  echo "Available browser_download_url entries:" >&2
  echo "$RESPONSE" | grep -o '"browser_download_url": *"[^"]*"' >&2
  exit 1
fi

echo "Downloading $APP_NAME from $VERSION_URL..."

# Download as root to persistent location
if ! curl --tlsv1.2 -x http://127.0.0.1:8082/ -L -o "$INSTALL_DIR/cryptomator.AppImage" "$VERSION_URL"; then
  echo "ERROR: Download failed." >&2
  exit 1
fi

# Verify file was actually downloaded
if [[ ! -f "$INSTALL_DIR/cryptomator.AppImage" ]]; then
  echo "ERROR: Download completed but file not found!" >&2
  exit 1
fi

echo "Download completed successfully. File size: $(du -h "$INSTALL_DIR/cryptomator.AppImage" | cut -f1)"

# Hash verification
echo "Verifying integrity..."

# Extract the hash from API
ASSET_NAME=$(basename "$VERSION_URL")

# Extract hash
EXPECTED_HASH=$(echo "$RESPONSE" | \
  grep -A 30 "\"name\": *\"$ASSET_NAME\"" | \
  grep '"digest"' | \
  head -n 1 | \
  sed -n 's/.*"digest": *"sha256:\([a-f0-9]*\)".*/\1/p')

if [[ -z "$EXPECTED_HASH" ]]; then
  echo "ERROR: Could not extract hash from release info!" >&2
  echo "Looking for asset: $ASSET_NAME" >&2
  echo "Available assets with digests:" >&2
  echo "$RESPONSE" | grep -B 5 -A 5 '"digest"' >&2
  rm -f "$INSTALL_DIR/cryptomator.AppImage"
  exit 1
fi

echo "Expected hash: $EXPECTED_HASH"
ACTUAL_HASH=$(sha256sum "$INSTALL_DIR/cryptomator.AppImage" | cut -d' ' -f1)
echo "Found hash:    $ACTUAL_HASH"

if [[ "$EXPECTED_HASH" != "$ACTUAL_HASH" ]]; then
  echo "ERROR: Hash verification failed!" >&2
  echo "Hashes do not match!" >&2
  rm -f "$INSTALL_DIR/cryptomator.AppImage"
  exit 1
fi

echo "Hash verification passed"

# Set permissions (for user execution in appvm and root ownership in template)
chmod 755  "$INSTALL_DIR/cryptomator.AppImage"
echo "$APP_NAME AppImage downloaded and verified successfully"

# List contents to verify
echo "Contents of $INSTALL_DIR:"
ls -la "$INSTALL_DIR"
EOF_CRYPTO

  # Copy and execute the download script in root space (template)
  qvm-copy-to-vm "$VAULT_TEMPLATE" "$temp_crypto_script"
  qvm-run -p -u root "$VAULT_TEMPLATE" "
  mv '/home/user/QubesIncoming/dom0/$(basename "$temp_crypto_script")' /download-cryptomator.sh
  chmod +x /download-cryptomator.sh
  bash /download-cryptomator.sh
  rm -f /download-cryptomator.sh
"

  # Clean up
  rm -f "$temp_crypto_script"
  qvm-run -p "$VAULT_TEMPLATE" "rm -rf '/home/user/QubesIncoming/'" 2>/dev/null || true

  # Create desktop entry in persistent location
  qvm-run -p -u root "$VAULT_TEMPLATE" "
  cat > /usr/share/applications/cryptomator.desktop </dev/null && mv logo.png cryptomator.png || touch cryptomator.png
  chmod 644 cryptomator.png
"

  echo "Cryptomator GUI installed successfully to persistent location"
}

# Step 3: Create cloud template and install MEGA GUI
create_cloud_template() {
  echo -e "\nStep 3: Installing MEGA GUI..."

  # Create Cloud Template (MEGAsync)
  if ! qvm-check "$CLOUD_TEMPLATE" 2>/dev/null; then
    echo "Creating Cloud Template..."
    qvm-clone "$BASE_TEMPLATE" "$CLOUD_TEMPLATE"
    qvm-prefs "$CLOUD_TEMPLATE" label green
  fi

  qvm-start --skip-if-running "$CLOUD_TEMPLATE"

  # Configure bind-dirs for MEGA
  configure_bind_dirs "$CLOUD_TEMPLATE" "mega"

  # Set environment
  qvm-run -p -u root "$CLOUD_TEMPLATE" "export DEBIAN_FRONTEND=noninteractive"
  qvm-run -p -u root "$CLOUD_TEMPLATE" "echo 'TERM=xterm' >> /etc/environment"
  qvm-run -p -u root "$CLOUD_TEMPLATE" "sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen"
  qvm-run -p -u root "$CLOUD_TEMPLATE" "locale-gen en_US.UTF-8"

  # Install dependencies IN TEMPLATE
  qvm-run -p -u root "$CLOUD_TEMPLATE" "
  apt-get install -y --no-install-recommends \
  qubes-core-agent-passwordless-root \
  qubes-core-agent-networking \
  curl \
  gpg \
  firefox-esr \
  thunar \
  qubes-core-agent-thunar \
  mousepad \
  less \
  psmisc \
  xfce4-terminal \
  libqt5core5a \
  libqt5dbus5 \
  libqt5gui5 \
  libqt5network5 \
  libqt5qml5 \
  libqt5quick5 \
  libqt5quickwidgets5 \
  libqt5svg5 \
  libqt5widgets5 \
  libqt5x11extras5 \
  libc-ares2 \
  qml-module-qtquick-dialogs \
  qml-module-qtquick-controls \
  qml-module-qtquick-controls2 \
  libicu72 \
  libdouble-conversion3 \
  libpcre2-16-0 \
  libxcb-xinerama0 \
  libssl3
"

# Create mimeapps.list with default MIME type associations
  local temp_mime_file=$(mktemp)
  cat > "$temp_mime_file" < "$temp_mega_script" <&2
  exit 1
fi

# Extract package details for amd64 architecture
echo "Extracting MEGAsync package details..."
PACKAGE_INFO=$(echo "$RESPONSE" | sed -n '/^Package: megasync$/,/^$/p' | grep -A 20 'Architecture: amd64')

if [[ -z "$PACKAGE_INFO" ]]; then
  echo "ERROR: Could not find megasync package for amd64 architecture in repository" >&2
  exit 1
fi

# Extract download URL for amd64
DOWNLOAD_URL=$(echo "$PACKAGE_INFO" | grep '^Filename:' | sed "s|^Filename: \\./|$MEGA_BASE_URL|" | tr -d '\n' | sed 's/[[:space:]]*$//')

# Show what URL was found
echo "Found URL: $DOWNLOAD_URL"

# If no URL was found, exit with error
if [[ -z "$DOWNLOAD_URL" ]]; then
  echo "ERROR: No valid download URL found for $APP_NAME." >&2
  echo "Available package info:" >&2
  echo "$PACKAGE_INFO" >&2
  exit 1
fi

# Extract version for display
VERSION=$(echo "$PACKAGE_INFO" | grep '^Version:' | cut -d' ' -f2)
echo "Found MEGAsync version: $VERSION"

echo "Downloading $APP_NAME from $DOWNLOAD_URL..."

# Download package
if ! curl --tlsv1.2 -x http://127.0.0.1:8082/ -L -o "$TEMP_DIR/megasync.deb" "$DOWNLOAD_URL"; then
  echo "ERROR: Download failed." >&2
  exit 1
fi

# Hash verification
echo "Verifying integrity..."

# Extract the hash from package info for amd64
EXPECTED_SHA256=$(echo "$PACKAGE_INFO" | grep '^SHA256:' | cut -d' ' -f2)

if [[ -z "$EXPECTED_SHA256" ]]; then
  echo "ERROR: Could not extract hash from package info!" >&2
  echo "Looking for SHA256 in package info:" >&2
  echo "$PACKAGE_INFO" | grep -A 5 -B 5 'SHA256' >&2
  rm -f "$TEMP_DIR/megasync.deb"
  exit 1
fi

echo "Expected hash: $EXPECTED_SHA256"
ACTUAL_SHA256=$(sha256sum "$TEMP_DIR/megasync.deb" | cut -d' ' -f1)
echo "Found hash:    $ACTUAL_SHA256"

if [[ "$EXPECTED_SHA256" != "$ACTUAL_SHA256" ]]; then
  echo "ERROR: Hash verification failed!" >&2
  echo "Hashes do not match!" >&2
  rm -f "$TEMP_DIR/megasync.deb"
  exit 1
fi

echo "Hash verification passed"

# Install the package
echo "Installing MEGAsync..."
if dpkg -i "$TEMP_DIR/megasync.deb"; then
  echo "MEGAsync installed successfully via dpkg"
elif apt-get install -f -y; then
  echo "MEGAsync installed successfully with dependency fix"
else
  echo "WARNING: MEGAsync installation had issues but continuing..."
fi

# Check if megasync command is available
if command -v megasync >/dev/null 2>&1 || dpkg -l | grep -q megasync; then
  echo 'MEGAsync installation verified successfully'
else
  echo 'WARNING: MEGAsync installation may have issues, but continuing setup' >&2
fi

# Clean up
rm -rf "$TEMP_DIR"
echo "$APP_NAME installation process completed"
EOF_MEGA

  # Copy and execute the install script in root space (template)
  qvm-copy-to-vm "$CLOUD_TEMPLATE" "$temp_mega_script"
  qvm-run -p -u root "$CLOUD_TEMPLATE" "
  mv '/home/user/QubesIncoming/dom0/$(basename "$temp_mega_script")' /install-megasync.sh
  chmod +x /install-megasync.sh
  bash /install-megasync.sh
  rm -f /install-megasync.sh
"

  # Clean up
  rm -f "$temp_mega_script"
  qvm-run -p "$CLOUD_TEMPLATE" "rm -rf '/home/user/QubesIncoming/'" 2>/dev/null || true

  echo "MEGA GUI installed successfully to persistent location"
}

# Step 4: Create AppVMs
create_appvms() {
  echo -e "\nStep 4: Creating AppVMs..."

  # Create Vault AppVM (air-gapped)
  if ! qvm-check "$VAULT_APPVM" 2>/dev/null; then
  echo "Creating Vault AppVM..."
  qvm-create --template "$VAULT_TEMPLATE" --label black \
  --property netvm="" \
  --property provides_network=False \
  --property vcpus=2 \
  --property memory=2048 \
  --property maxmem=4096 \
  --property autostart=False \
  "$VAULT_APPVM"

  # Create vault directories in user space (AppVM)
  qvm-run -p "$VAULT_APPVM" "
  mkdir -p /home/user/Vaults /home/user/Mounts
  chown -R user:user /home/user/Vaults /home/user/Mounts
"
  # Extend storage
  qvm-shutdown --wait "$VAULT_APPVM" 2>/dev/null || true
  qvm-volume extend "$VAULT_APPVM:private" "$PRIV_VOL_SIZE" 2>/dev/null || echo "Warning: Could not extend vault volume"
  fi

  # Create Cloud AppVM (network-enabled)
  if ! qvm-check "$CLOUD_APPVM" 2>/dev/null; then
  echo "Creating Cloud AppVM..."
  qvm-create --template "$CLOUD_TEMPLATE" --label green \
  --property netvm="$CLOUD_APPVM_NETVM" \
  --property provides_network=False \
  --property vcpus=2 \
  --property memory=1536 \
  --property maxmem=3072 \
  --property autostart=False \
  "$CLOUD_APPVM"
  fi
}

# Step 5: Configure Qubes policies
configure_qubes_policies() {
  echo -e "\nStep 5: Configuring Qubes policies..."

  local policy_file="/etc/qubes/policy.d/30-gui-cloud-backup.policy"

  sudo tee "$policy_file" > /dev/null < "$temp_vault_mgr_script" </dev/null || true

  # Cloud Manager script for Cloud AppVM in the root space (template)
  local temp_cloud_mgr_script=$(mktemp)
  cat > "$temp_cloud_mgr_script" </dev/null)" ]; then
  echo "Found backup files in incoming directory:"
  ls -la "$INCOMING_DIR"

  # Move backups to MEGA sync directory
  mkdir -p "/home/user/MEGA/Backups"
  find "$INCOMING_DIR" -name "*.tar.gz" -exec mv {} "/home/user/MEGA/Backups/" \;

  echo "Backup files moved to MEGA sync directory."
  echo "MEGAsync will automatically sync these to your MEGA cloud."

  # Clean up
  rm -rf "$INCOMING_DIR"
  else
  echo "No backup files found in incoming directory."
  fi
}

open_mega_folder() {
  echo "Opening MEGA folder in Thunar..."
  thunar "/home/user/MEGA" &
}

setup_mega_sync() {
  echo "MEGAsync Setup Instructions:"
  echo ""
  echo "1. Start MEGAsync from Applications menu"
  echo "2. Log in with your MEGA credentials"
  echo "3. Add sync pairs:"
  echo "   - Local: /home/user/MEGA"
  echo "   - Remote: / (or specific folder in MEGA cloud)"
  echo "4. Enable sync for automatic uploads"
  echo ""
  echo "Backups from Vault AppVM will be automatically synced to MEGA."
}

# Main menu
while true; do
  echo ""
  echo "Cloud Manager"
  echo "1. Process Backups"
  echo "2. Open MEGA Folder"
  echo "3. MEGA Setup Help"
  echo "4. Show Help"
  echo "5. Exit"
  echo ""
  read -p "Choose an option (1-5): " CHOICE

  case "$CHOICE" in
  1)
    process_backups
    ;;
  2)
    open_mega_folder
    ;;
  3)
    setup_mega_sync
    ;;
  4)
    show_help
    ;;
  5|"")
    break
    ;;
  *)
    echo "Invalid option"
    ;;
  esac
done
EOF_CLOUD_MGR

  qvm-copy-to-vm "$CLOUD_TEMPLATE" "$temp_cloud_mgr_script"
  qvm-run -p -u root "$CLOUD_TEMPLATE" "
  mkdir -p /opt
  mv '/home/user/QubesIncoming/dom0/$(basename "$temp_cloud_mgr_script")' /opt/cloud-manager
  chmod +x /opt/cloud-manager
  chown user:user /opt/cloud-manager
"

  # Clean up
  rm -f "$temp_cloud_mgr_script"
  qvm-run -p "$CLOUD_TEMPLATE" "rm -rf '/home/user/QubesIncoming/'" 2>/dev/null || true

  echo "Integration scripts installed to persistent locations"
}

# Step 7: Create desktop entries
create_desktop_entries() {
  echo -e "\nStep 7: Creating desktop entries..."

  # Vault Manager desktop entry in the root space (template)
  local temp_vault_desktop=$(mktemp)
  cat > "$temp_vault_desktop" </dev/null || true

  # Cloud Manager desktop entry
  local temp_cloud_desktop=$(mktemp)
  cat > "$temp_cloud_desktop" </dev/null || true

  echo "Desktop entries created in persistent locations"
}

# Step 8: Final setup
finalize_setup() {
  echo -e "\nStep 8: Finalizing setup..."

  # Sync appmenus and shutdown
  qvm-start --skip-if-running "$VAULT_TEMPLATE"
  qvm-sync-appmenus "$VAULT_TEMPLATE"
  qvm-shutdown --wait "$VAULT_TEMPLATE"

  qvm-start --skip-if-running "$CLOUD_TEMPLATE"
  qvm-sync-appmenus "$CLOUD_TEMPLATE"
  qvm-shutdown --wait "$CLOUD_TEMPLATE"

  qvm-start --skip-if-running "$VAULT_APPVM"
  qvm-sync-appmenus "$VAULT_APPVM"
  qvm-shutdown --wait "$VAULT_APPVM"

  qvm-start --skip-if-running "$CLOUD_APPVM"
  qvm-sync-appmenus "$CLOUD_APPVM"
  qvm-shutdown --wait "$CLOUD_APPVM"

  # Configure menu items using desktop entries from persistent locations (template)
  qvm-features "$VAULT_TEMPLATE" menu-items "xfce4-terminal.desktop thunar.desktop org.xfce.mousepad.desktop"
  qvm-features "$CLOUD_TEMPLATE" menu-items "xfce4-terminal.desktop thunar.desktop org.xfce.mousepad.desktop"
  qvm-features "$VAULT_APPVM" menu-items "cryptomator.desktop vault-manager.desktop thunar.desktop org.xfce.mousepad.desktop xfce4-terminal.desktop"
  qvm-features "$CLOUD_APPVM" menu-items "cloud-manager.desktop firefox-esr.desktop megasync.desktop thunar.desktop org.xfce.mousepad.desktop xfce4-terminal.desktop"
}

# Main installation function
main() {
  echo "Starting Cloud Backup Setup..."

  create_base_template
  create_vault_template
  download_cryptomator
  create_cloud_template
  download_install_megasync
  create_appvms
  configure_qubes_policies
  create_integration_scripts
  create_desktop_entries
  finalize_setup

  echo -e "\nSetup completed successfully!"
}

# Run main function
main "$@"

Complex scripts dont render correctly, so:

qubes-gui-cloud-backup.sh.log (25.7 KB)



References

Core References

Qubes OS Backup Setups

Qubes OS Forum

Qubes OS Documentation


RCLONE References

MEGA References

gocryptfs References

Cryptomator References

Other references