Templates

Contributing

GitHub
Guidelines and checklist for submitting new templates.
The built-in catalog is community-driven. Want to contribute an app? Open a PR on GitHub. You can also add custom catalogs from public Git repositories or local folders in Lawn Settings.

Templates live in the getlawn/templates repository. This page covers the conventions and requirements for contributing a new template via pull request.

Naming Conventions

App ID — Lowercase, hyphen-separated. Must match the directory name and the id field in manifest.yaml. Use the app's commonly known name (e.g., paperless-ngx, uptime-kuma, adguard-home).

Compose filename. Use lawn-compose/compose.yaml.

Icon Requirements

Include an icon.svg (preferred), icon.png, icon.heif, or icon.pdf in the template directory. SVG is preferred because it scales to any size. For PNG, use at least 256×256 pixels.

The icon field in manifest.yaml specifies an SF Symbol name used as a fallback when no icon file is present.

Style Guidelines

  • Localization — All user-facing strings (display names, descriptions, labels, notice titles) belong in locales/en.json, not in the YAML template.
  • Notices — Use a maximum of 2-3 per template. Reserve them for information users genuinely need to act on: login credentials, required setup steps, or warnings about values they should not change. Use info for general guidance and warning for critical actions.
  • Environment defaults — Always provide ${VAR:-default} fallbacks so the compose file works standalone with docker compose config.
  • Descriptions — Write clear, concise descriptions for variables and environment metadata. Describe what the value controls, not just what it is.
  • Display ordering — Use weight to order config variables logically. Group related settings together and put important ones first.
  • Volumes — Use relative paths (./data:/app/data) for persistent data. Avoid absolute paths unless strictly necessary.

PR Checklist

Before submitting a template, verify each item:

Manifest

  • id matches the directory name
  • name, description, icon, and category are set
  • category is one of: Documents, Media, Development, Networking, Utilities, Databases, Monitoring
  • webPort is set if the app has a web UI
  • At least one link (GitHub or documentation) is included

Compose File

  • All images use explicit version tags (no latest)
  • Compose file validates with docker compose config
  • Environment variables use ${VAR:-default} substitution with sensible defaults
  • Volumes use relative paths for persistent data
  • Ports are declared for services with a web UI

Variables and Secrets

  • Passwords and tokens use generatedSecret (not hardcoded defaults)
  • Generated secrets declare the appropriate format and use length or bytes as required
  • userProvided variables are only used when the user must supply the value (API keys, external URLs)
  • inputType is set for boolean and number config variables
  • Config options use compose overlays for each selectable value

Localization

  • locales/en.json exists with entries for all user-facing strings
  • Every config variable has displayName and description
  • Every config option value has a label
  • Boolean config variables have exactly 2 option labels ("true" and "false")
  • Every environment variable has a description
  • Every health check has a description
  • Every notice has a title and all elements have label/content
  • No dangling keys (JSON entries without matching YAML structures)

Health Checks

  • At least one Lawn health check (x-lawn.healthChecks) is defined for the main service
  • HTTP checks use a reliable endpoint (health or root path)
  • Database services have a standard healthcheck in the compose spec

Notices

  • Login credentials are surfaced in a notice (if applicable)
  • Notices use sourceField for dynamic values instead of hardcoding passwords
  • No more than 2-3 notices per template

Testing

  • lawn template validate . passes (catches missing localization entries)
  • lawn template test . passes (with --var for any userProvided variables)
  • All health checks pass within the default timeout