Skip to main content

installation.yaml Reference

The installation.yaml file is the heart of your extension. It tells Sulla Desktop how to install, configure, start, stop, and manage your extension.

Full Schema

# Required — unique identifier for your extension
id: stirling-pdf

# Required — display name shown in the UI
name: Stirling-PDF

# Required — short description of what your extension does
description: Full-featured open-source PDF toolbox — merge, split, compress, OCR, sign, convert, and more

# Required — icon filename (must exist in your recipe folder)
icon: stirling.svg

# Required — semantic version string
version: '2026.02'

# Required — category for catalog filtering
category: productivity

# Compose configuration
compose:
composeFile: 'docker-compose.yml' # Path to your compose file (default: docker-compose.yml)
envFile: '.env' # Optional .env file for docker compose

# Setup commands run in order during installation
setup:
- command: "echo 'Installing...'"
- command: 'chmod +x ${APP_DIR}/some-script.sh'
- command: '${APP_DIR}/some-script.sh'
optional: false # If false, failure blocks the install. Default: true

# Runtime commands — how Sulla manages your extension
commands:
start: 'docker compose -f ${COMPOSE_FILE} up -d'
stop: 'docker compose -f ${COMPOSE_FILE} down'
restart: 'docker compose -f ${COMPOSE_FILE} restart'
status: 'docker compose -f ${COMPOSE_FILE} ps'
update: 'docker compose -f ${COMPOSE_FILE} pull && docker compose -f ${COMPOSE_FILE} up -d'
logs: 'docker compose -f ${COMPOSE_FILE} logs --tail=100'

# Port your extension listens on (informational)
defaultPort: 30201

# URL paths for browser integration
adminPath: '/admin'
webmailPath: '/webmail'
openInBrowser: true

# Extra URLs — shown in the UI card dropdown and system tray
extraUrls:
- label: 'Dashboard'
url: 'http://localhost:30201'
- label: 'Admin Panel'
url: 'http://localhost:30201/admin'

# Environment variables — defaults that users can override
env:
DOMAIN: 'localhost'
ENABLE_SSL: 'false'

Field Reference

Required Fields

FieldTypeDescription
idstringUnique identifier. Use lowercase, hyphens only. Must match your recipe folder name.
namestringHuman-readable display name.
descriptionstringOne-line description shown in the catalog.
iconstringFilename of your icon asset in the recipe folder.
versionstringVersion of your extension. Use semantic versioning.
categorystringPrimary category: productivity, email, media, communication, business, development, security, utility-tools, etc.

compose

Tells Sulla where your Docker Compose file is.

FieldTypeDefaultDescription
composeFilestringdocker-compose.ymlPath to your compose file, relative to ${APP_DIR}.
envFilestringOptional .env file to pass to docker compose --env-file.

If your extension ships its compose file in the recipe folder (recommended), the default docker-compose.yml just works. If you use git clone in your setup to pull a repo, point composeFile to the right path within that clone.

setup

An ordered list of shell commands to run during installation. Each step runs via /bin/sh -c <command> with cwd set to the extension directory (${APP_DIR}).

FieldTypeDefaultDescription
commandstringrequiredThe shell command to run.
cwdstring${APP_DIR}Working directory for this command. Supports ${APP_DIR} substitution.
optionalbooleantrueIf true, a failure logs a warning but installation continues. If false, failure aborts the install.

Setup steps are optional by default. This is intentional — interactive scripts (like config generators that prompt for user input) will fail in automated mode, and you don't want that to block installation.

If a step is critical (e.g. creating a required config file), set optional: false.

Shell Variable Substitution

These variables are replaced in all command and cwd values:

VariableResolves To
${APP_DIR}The extension's local directory on disk
${COMPOSE_FILE}Full absolute path to the compose file

Note: Paths containing spaces are automatically shell-quoted when substituted into commands.

Examples

Simple — no setup needed (compose file is in the recipe folder):

setup: []

Git clone a repo, then run a config script:

setup:
- command: 'git clone --branch master --depth 1 https://github.com/example/app.git ${APP_DIR}/repo'
optional: false
- command: 'cd ${APP_DIR}/repo && ./generate_config.sh'
optional: true

Download a file and set permissions:

setup:
- command: 'curl -sL https://example.com/config.tar.gz | tar xz -C ${APP_DIR}'
optional: false
- command: 'chmod +x ${APP_DIR}/entrypoint.sh'

commands

Shell commands Sulla uses to manage your extension at runtime. All commands support ${APP_DIR} and ${COMPOSE_FILE} substitution. They run with cwd set to ${APP_DIR}.

FieldTypeDescription
startstringStart your containers.
stopstringStop your containers.
restartstringRestart your containers. If omitted, Sulla runs stop then start.
statusstringCheck if your containers are running.
updatestringPull latest images and restart.
logsstringTail recent container logs.

The standard pattern for compose-based extensions:

commands:
start: 'docker compose -f ${COMPOSE_FILE} up -d'
stop: 'docker compose -f ${COMPOSE_FILE} down'
restart: 'docker compose -f ${COMPOSE_FILE} restart'
status: 'docker compose -f ${COMPOSE_FILE} ps'
update: 'docker compose -f ${COMPOSE_FILE} pull && docker compose -f ${COMPOSE_FILE} up -d'
logs: 'docker compose -f ${COMPOSE_FILE} logs --tail=100'

If your extension uses a git-cloned repo, point to the compose file in that subdirectory:

commands:
start: 'docker compose -f ${APP_DIR}/repo/docker-compose.yml up -d'
stop: 'docker compose -f ${APP_DIR}/repo/docker-compose.yml down'

extraUrls

A list of URLs that your extension registers. These appear in two places:

  1. Extension card — a dropdown "Open" button on the installed extension card in the UI
  2. System tray — under Tray > Extensions > Your Extension Name, each URL is a clickable menu item
FieldTypeDescription
labelstringDisplay label for the link (e.g. "Dashboard", "Webmail").
urlstringFull URL including protocol and port.
extraUrls:
- label: 'Stirling-PDF Dashboard'
url: 'http://localhost:30201'

Users see this in the system tray as:

Tray Icon
└── Extensions ▸
└── Stirling-PDF ▸
└── Stirling-PDF Dashboard ← clicks open browser

And on the extension card as a green "Open ▾" dropdown button.

env

Environment variables for your extension. Sulla writes these as a .env file in the extension directory before each start. Docker Compose reads .env automatically.

Values support {{variable}} substitution (see Variable Reference below).

env:
DOMAIN: 'localhost'
DB_HOST: 'sulla-mariadb'
DB_NAME: 'myapp'
ADMIN_EMAIL: '{{sullaEmail}}'
APP_SECRET: '{{sullaN8nEncryptionKey}}'
SLACK_TOKEN: '{{SLACK.BOT_KEY}}'

The .env file is refreshed on every start, so if the user changes their settings or integration credentials, the new values take effect on the next restart.


Variable Reference

Sulla Desktop supports {{variable}} placeholders in two places:

  1. docker-compose.yml — resolved once at install time, written to disk
  2. env field — resolved on every start, written as .env

Unresolvable placeholders are left as-is (not removed), so you can debug missing values easily.

Sulla Settings Variables

Access any property from the user's Sulla Settings (stored in PostgreSQL/Redis, bootstrapped from the fallback JSON file).

SyntaxDescription
{{sullaEmail}}User's email address
{{sullaPassword}}User's Sulla account password
{{sullaServicePassword}}Auto-generated service password (used for internal databases, APIs)
{{sullaN8nEncryptionKey}}Auto-generated encryption key (base64)
{{sullaModel}}Default AI model (e.g. qwen2:0.5b)
{{primaryUserName}}User's display name
{{pathUserData}}Path to user data directory

Any property stored in SullaSettingsModel can be referenced by its exact key name.

Integration Variables

Access credentials and config from connected integrations. Format: {{INTEGRATION_ID.PROPERTY}}.

SyntaxDescription
{{SLACK.BOT_KEY}}Slack bot token
{{SLACK.WEBHOOK_URL}}Slack webhook URL
{{GITHUB.ACCESS_TOKEN}}GitHub personal access token
{{SMTP.HOST}}SMTP server hostname
{{SMTP.USERNAME}}SMTP username
{{SMTP.PASSWORD}}SMTP password

The INTEGRATION_ID and PROPERTY match whatever the user configured in Sulla Desktop's Integrations panel. Any integration_id.property pair stored in the IntegrationService is accessible.

Built-in Path Variables

Auto-resolved from the user's operating system. Directories are created automatically if they don't exist.

VariablemacOS ExampleDescription
{{path.home}}/Users/jonathonUser's home directory
{{path.documents}}/Users/jonathon/DocumentsDocuments folder
{{path.downloads}}/Users/jonathon/DownloadsDownloads folder
{{path.desktop}}/Users/jonathon/DesktopDesktop folder
{{path.movies}}/Users/jonathon/MoviesMovies folder
{{path.music}}/Users/jonathon/MusicMusic folder
{{path.pictures}}/Users/jonathon/PicturesPictures folder
{{path.data}}<extension_dir>/dataExtension's persistent data directory (survives uninstall)
{{path.appdir}}<extension_dir>Extension's install directory (same as ${APP_DIR})

Modifiers

Pipe a value through a modifier to transform it before substitution. Syntax: {{variable|modifier}}.

ModifierEffectExampleResult
urlencodePercent-encode special characters{{sullaServicePassword|urlencode}}f%40ze4C7IPbgWSRHR
base64Base64-encode{{sullaServicePassword|base64}}ZkB6ZTRDNy4uLg==
quoteWrap in escaped single quotes{{path.movies|quote}}'/Users/me/Movies'
jsonJSON-stringify (with quotes){{sullaEmail|json}}"[email protected]"

When to use modifiers:

  • urlencode — Always use when embedding passwords or values with special characters (@, /, #, ?, &, =, +, spaces) inside URLs or connection strings:

    PG_DATABASE_URL: postgres://user:{{sullaServicePassword|urlencode}}@db:5432/mydb
  • base64 — Use for secrets that need base64 encoding:

    APP_SECRET: '{{sullaN8nEncryptionKey|base64}}'
  • quote — Use when a path might contain spaces and is used in a shell context:

    setup:
    - command: 'ls {{path.documents|quote}}'
  • json — Use when embedding a value in a JSON config file:

    setup:
    - command: 'echo ''{"email": {{sullaEmail|json}}}'' > ${APP_DIR}/config.json'

Variable Syntax Quick Reference

ContextSyntaxExample
Shell commands (setup, commands)${VAR}${APP_DIR}, ${COMPOSE_FILE}
Docker Compose files{{var}}{{sullaEmail}}, {{path.movies}}
env field in installation.yaml{{var}}{{sullaServicePassword|urlencode}}
With modifier{{var|mod}}{{sullaServicePassword|urlencode}}
Integration values{{ID.PROP}}{{SLACK.BOT_KEY}}
Path variables{{path.name}}{{path.data}}

Important: ${VAR} and {{var}} are two different systems. ${VAR} is for shell commands only. {{var}} is for compose files and env values only. Do not mix them.


Data Persistence

Extensions have a data/ subdirectory inside their install directory ({{path.data}}). This directory is preserved when the user uninstalls the extension (unless they explicitly check "Delete my data" in the uninstall confirmation dialog).

Use {{path.data}} for bind-mount volumes that should survive uninstall/reinstall:

volumes:
- {{path.data}}/config:/config
- {{path.data}}/storage:/data

Browser Integration Fields

FieldTypeDefaultDescription
defaultPortnumberThe primary port your extension listens on.
adminPathstringURL path to admin panel (e.g. /admin).
webmailPathstringURL path to webmail (e.g. /webmail).
openInBrowserbooleanfalseWhether to auto-open the extension in the browser after start.

Minimal Example

The simplest possible installation.yaml:

id: my-extension
name: My Extension
description: A simple web app
icon: icon.png
version: '1.0.0'
category: utility-tools

setup: []

commands:
start: 'docker compose -f ${COMPOSE_FILE} up -d'
stop: 'docker compose -f ${COMPOSE_FILE} down'

extraUrls:
- label: 'Open My Extension'
url: 'http://localhost:8080'

Pair this with a docker-compose.yml in your recipe folder and you're done.