Contributing
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 filenames — Use v followed by the app version: v1.0.0.yaml, v10.10.yaml, or v32.yaml. The version can have one, two, or three parts depending on the app's versioning scheme.
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) -
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