Templates
Contributing
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
infofor general guidance andwarningfor critical actions. - Environment defaults — Always provide
${VAR:-default}fallbacks so the compose file works standalone withdocker 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
weightto 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
-
idmatches the directory name -
name,description,icon, andcategoryare set -
categoryis one of: Documents, Media, Development, Networking, Utilities, Databases, Monitoring -
webPortis 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
formatand uselengthorbytesas required -
userProvidedvariables are only used when the user must supply the value (API keys, external URLs) -
inputTypeis set for boolean and number config variables - Config options use compose overlays for each selectable value
Localization
-
locales/en.jsonexists with entries for all user-facing strings - Every config variable has
displayNameanddescription - 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
titleand all elements havelabel/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
healthcheckin the compose spec
Notices
- Login credentials are surfaced in a notice (if applicable)
- Notices use
sourceFieldfor 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--varfor anyuserProvidedvariables) - All health checks pass within the default timeout
Continue reading