---
title: "Update Strategy Reference"
description: "Choose how fp-appimage-updater finds and downloads updates for each app."
---

Every app recipe requires a `strategy` block that tells fp-appimage-updater where to look for the latest version and how to download it. Three strategies are available.

<Note>
  **When to use each strategy:**

  | Strategy | Use when |
  |----------|----------|
  | `forge` | The app publishes releases on GitHub or GitLab |
  | `direct` | The app provides a stable URL that always points to the latest version |
  | `script` | Neither of the above fits — you need custom logic to find the download URL |
</Note>

<Tabs>
  <Tab title="Forge">
    Use the `forge` strategy for apps distributed through GitHub, GitLab, Gitea, or Forgejo releases. fp-appimage-updater queries the releases API, matches the asset filename against your pattern, and downloads the matching file.

    For GitHub repos, you can provide an API token via `secrets.yml` or the `GITHUB_TOKEN` environment variable to bypass rate limits. See [Global Config](/configuration/global-config#github-rate-limits--tokens) for details.

    For GitLab repositories, the resolver uses the permalink latest API at `https://gitlab.com/api/v4/projects/<project-path>/releases/permalink/latest`, reads `assets.links`, and prefers `direct_asset_url` when available.

    For self-hosted Gitea or Forgejo instances, the resolver automatically detects the platform by querying the `/swagger.v1.json` metadata endpoint of the repository's host.

    ### Fields

    <ParamField path="strategy.strategy" type="string" required>
      Must be `forge`.
    </ParamField>

    <ParamField path="strategy.repository" type="string" required>
      Full URL to the GitHub, GitLab, Gitea, or Forgejo repository (e.g., `https://github.com/owner/repo`).
    </ParamField>

    <ParamField path="strategy.asset_match" type="string">
      Wildcard pattern to match the release asset filename. Uses shell-style glob syntax (e.g., `"*-amd64.AppImage"`). The first matching asset is selected. Required if `asset_match_regex` is not provided.
    </ParamField>

    <ParamField path="strategy.asset_match_regex" type="string">
      Optional regular expression to match the asset filename. Use this for complex cases where a simple glob pattern is too broad. For example, to match `Obsidian-1.5.3.AppImage` while excluding `Obsidian-1.5.3-arm64.AppImage`, you can use `^Obsidian-[0-9.]+\\.AppImage$`.
    </ParamField>

    <ParamField path="strategy.inner_asset_match" type="string">
      Optional pattern to find a specific AppImage inside a `.zip` archive. If the resolved asset is a ZIP file, the updater will extract the file matching this pattern. If omitted, it automatically searches for files ending in `.AppImage` or containing ELF magic bytes.
    </ParamField>

    ### Per-app overrides

    You can also set these fields directly on the app recipe (not inside `strategy`) to override the global config for this app:

    <ParamField path="github_proxy" type="boolean">
      Enable or disable the GitHub API proxy fallback for this app. When enabled, a rate-limited GitHub metadata request is retried through the configured proxy bases. The actual download URL always uses the direct GitHub asset link.
    </ParamField>

    <ParamField path="github_proxy_prefix" type="string | string[]">
      Proxy base URL(s) to use when `github_proxy` is enabled. Accepts a single URL string, a list of URLs, or the string `all` to try every built-in proxy in order.
    </ParamField>

    <ParamField path="respect_rate_limits" type="boolean">
      When `false`, fp-appimage-updater ignores a prior rate limit window and always attempts the request.
    </ParamField>

    ### Examples

    **Standard Forge release:**
    ```yaml
    name: hydra-launcher
    strategy:
      strategy: forge
      repository: https://github.com/hydralauncher/hydra
      asset_match: "hydralauncher-*.AppImage"
    segmented_downloads: true
    ```

    **ZIP archive extraction:**
    If an app is packaged inside a ZIP file, use `asset_match` to find the ZIP and `inner_asset_match` to specify which file to extract:
    ```yaml
    name: sioyek
    strategy:
      strategy: forge
      repository: https://github.com/ahrm/sioyek
      asset_match: "sioyek-release-linux.zip"
      inner_asset_match: "Sioyek-x86_64.AppImage"
    ```

    **Regex matching:**
    Use `asset_match_regex` for complex cases where a simple glob pattern is too broad (e.g., to exclude `arm64` assets):
    ```yaml
    name: obsidian
    strategy:
      strategy: forge
      repository: "https://github.com/obsidianmd/obsidian-releases"
      asset_match_regex: "^Obsidian-[0-9.]+\\.AppImage$"
    ```

    **GitLab repository:**
    ```yaml
    name: glab
    strategy:
      strategy: forge
      repository: https://gitlab.com/gitlab-org/cli
      asset_match: "glab_*_linux_amd64.AppImage"
    ```

    **Self-hosted Gitea/Forgejo:**
    The host platform is automatically detected.
    ```yaml
    name: my-private-app
    strategy:
      strategy: forge
      repository: https://gitea.example.com/owner/repo
      asset_match: "app-*.AppImage"
    ```
  </Tab>

  <Tab title="Direct">
    Use the `direct` strategy when the app vendor publishes a stable URL that always resolves to the latest version. fp-appimage-updater checks the HTTP response headers to detect whether the remote file has changed since the last download.

    ### Fields

    <ParamField path="strategy.strategy" type="string" required>
      Must be `direct`.
    </ParamField>

    <ParamField path="strategy.url" type="string" required>
      Static download URL that always points to the latest version of the AppImage.
    </ParamField>

    <ParamField path="strategy.check_method" type="string" required>
      HTTP header used to detect remote changes. Accepted values:

      - `etag` — compares the `ETag` response header
      - `last-modified` — compares the `Last-Modified` response header

      Choose whichever header the server provides. If unsure, try `etag` first.
    </ParamField>

    ### Example

    ```yaml
    name: whatpulse
    strategy:
      strategy: direct
      url: "https://releases.whatpulse.org/latest/linux/whatpulse-linux-latest_amd64.AppImage"
      check_method: etag
    segmented_downloads: true
    ```
  </Tab>

  <Tab title="Script">
    Use the `script` strategy when neither `forge` nor `direct` fits — for example, when the app's download URL requires parsing a custom API response or a non-standard releases page.

    You provide a bash script that fp-appimage-updater executes to resolve the current download URL and version. The script must print exactly two lines to stdout:

    1. The download URL
    2. The version string

    fp-appimage-updater stores the version string locally and compares it on the next run to decide whether an update is needed.

    ### Fields

    <ParamField path="strategy.strategy" type="string" required>
      Must be `script`.
    </ParamField>

    <ParamField path="strategy.script_path" type="string" required>
      Path to the bash script, relative to the recipe's directory.
    </ParamField>

    <Note>
      `script_path` is resolved relative to the directory containing the recipe file. If your recipe is at `~/.config/fp-appimage-updater/apps/hayase/hayase.yml`, then `./resolver.sh` resolves to `~/.config/fp-appimage-updater/apps/hayase/resolver.sh`.
    </Note>

    ### Example

    ```yaml
    name: hayase
    strategy:
      strategy: script
      script_path: ./resolver.sh
    ```

    ```bash
    #!/usr/bin/env bash

    set -e

    # Fetch the YAML definition
    YAML_DATA=$(curl -sL "https://api.hayase.watch/files/latest-linux.yml")

    # Use yq or grep to extract version - let's write it generically for systems without yq
    VERSION=$(echo "$YAML_DATA" | grep "^version:" | awk '{print $2}')
    FILENAME=$(echo "$YAML_DATA" | grep "'linux-hayase-.*-linux.AppImage'" || echo "$YAML_DATA" | grep 'path:' | awk '{print $2}')
    # fallback using basic sed
    if [ -z "$FILENAME" ]; then
       FILENAME="linux-hayase-${VERSION}-linux.AppImage"
    fi

    DOWNLOAD_URL="https://api.hayase.watch/files/${FILENAME}"

    # The updater expects "DOWNLOAD_URL" then "VERSION" on consecutive lines of stdout:
    echo "$DOWNLOAD_URL"
    echo "$VERSION"
    ```
  </Tab>
</Tabs>
