Skip to main content

Building Extensions for Sulla Desktop

Extensions are self-contained applications that run as Docker containers inside Sulla Desktop. You provide a Docker Compose recipe and an installation manifest — Sulla handles the rest.

How It Works​

┌──────────────────────────────────────────────────────┐
│ Sulla Desktop │
│ │
│ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ Extension │ │ Your Docker Containers │ │
│ │ Manager │──▶│ (from your docker-compose) │ │
│ └──────┬──────┘ └─────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ installation │ ← Your recipe tells Sulla what │
│ │ .yaml │ to run and how to manage it │
│ └──────────────┘ │
└──────────────────────────────────────────────────────┘

Your extension is just a Docker Compose stack. Sulla Desktop:

  1. Downloads your recipe files from the sulla-recipes repository
  2. Runs any setup commands you define (e.g. git clone, config generation)
  3. Starts your containers with docker compose up -d
  4. Registers your URLs in the app UI and system tray
  5. Manages the lifecycle (start, stop, restart, update, uninstall)

Repository Structure​

All extensions live in the merchantprotocol/sulla-recipes repository:

sulla-recipes/
├── index.yaml # Master catalog (all extensions listed here)
└── recipes/
├── stirling-pdf/
│ ├── installation.yaml # How Sulla installs & runs your extension
│ ├── manifest.yaml # Catalog metadata (labels, description, etc.)
│ ├── docker-compose.yml # Your Docker Compose recipe
│ └── stirling.svg # Your extension icon
├── mailcow/
│ ├── installation.yaml
│ ├── manifest.yaml
│ └── cow_mailcow.svg
└── your-extension/
├── installation.yaml
├── manifest.yaml
├── docker-compose.yml
└── icon.png

Key Files​

FilePurposeRequired
installation.yamlTells Sulla how to install, start, stop, and manage your extensionYes
manifest.yamlCatalog metadata — title, description, icon, categoriesYes
docker-compose.ymlYour Docker Compose stackYes (unless using git clone)
Icon file (.png, .svg)Extension icon for the UI and catalogRecommended

Architecture Principles​

Your extension is a Docker container​

That's it. Write a docker-compose.yml that defines your services, volumes, ports, and environment variables. Sulla pulls and runs it exactly as you define.

Shared infrastructure​

Sulla Desktop may already be running common infrastructure containers (MySQL, Redis, Nginx, etc.). Your docker-compose.yml can connect to these shared resources by referencing their Docker networks. This avoids duplicating databases and services that the user already has running.

For example, if Sulla already runs a MariaDB instance, your extension can use it instead of spinning up its own:

services:
my-app:
image: my-app:latest
environment:
- DB_HOST=sulla-mariadb
- DB_PORT=3306
networks:
- sulla-shared

networks:
sulla-shared:
external: true

Variable substitution powers your config​

Sulla Desktop resolves {{variables}} in your docker-compose.yml and env field at install time, and refreshes the .env file on every start. This means your recipe can reference user-specific settings, integration credentials, and local filesystem paths — all without the user editing config files.

Three namespaces are available:

SyntaxSourceExample
{{propertyName}}Sulla Settings{{sullaEmail}} → [email protected]
{{INTEGRATION.PROP}}Integration Service{{SLACK.BOT_KEY}} → xoxb-...
{{path.name}}Built-in user paths{{path.movies}} → /Users/me/Movies

Values can be piped through modifiers for safe embedding:

ModifierEffectUse case
urlencodePercent-encodePasswords in connection strings
base64Base64-encodeSecrets in config files
quoteShell single-quotePaths with spaces in shell commands
jsonJSON-stringifyValues in JSON config
# docker-compose.yml
environment:
ADMIN_EMAIL: "{{sullaEmail}}"
PG_URL: "postgres://app:{{sullaServicePassword|urlencode}}@db:5432/myapp"
volumes:
- {{path.movies}}:/data/movies
- {{path.music}}:/data/music

See the full Variable Reference for all available variables.

Environment variables​

Define expected environment variables in the env section of installation.yaml. Sulla writes them as a .env file in the extension directory — Docker Compose reads it automatically. The .env is refreshed on every start, so setting changes take effect on restart.

env:
DOMAIN: 'localhost'
DB_HOST: 'sulla-mariadb'
ADMIN_EMAIL: '{{sullaEmail}}'
ENABLE_SSL: 'false'

Commands are just shell commands​

Sulla doesn't interpret your commands — it just runs them. The setup section and commands section are both plain shell commands executed via /bin/sh -c. You have full control.

Two variables are substituted before execution:

VariableResolves To
${APP_DIR}The extension's local directory (e.g. ~/Library/Application Support/sulla-desktop/extensions/stirling-pdf/)
${COMPOSE_FILE}Full path to your compose file (e.g. ${APP_DIR}/docker-compose.yml)

Note: ${VAR} syntax is for shell command substitution in setup and commands. {{var}} syntax is for docker-compose files and env values.

Quick Start​

The fastest way to build an extension:

  1. Create your recipe folder in recipes/your-extension/
  2. Write a docker-compose.yml that runs your app
  3. Write an installation.yaml that tells Sulla how to manage it
  4. Write a manifest.yaml with your catalog metadata
  5. Add an icon (PNG recommended for tray compatibility)
  6. Add your entry to index.yaml in the repo root
  7. Submit a pull request to sulla-recipes

See Submitting Your Extension for the full walkthrough.

Documentation​