DevelopmentUnreleased

This topic is not available in this guide. You're viewing its overview.

Development documentation may include features that are not in the latest release. Read the Lawn 0.8.1 guide.

Templates

Complete Example

A full template showing all features working together.
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.

Example template

This page walks through a complete template that uses every major feature. It covers a multi-service app with a PostgreSQL database, generated secrets, user-provided variables, config options (a log level picker and a boolean toggle), health checks, notices, environment metadata, init files, resource limits, and localization.

Compose YAML defines structure and behavior, while locales/en.json provides configuration and extension strings. App names, taglines, and descriptions remain in manifest.yaml.

Directory Structure

apps/
└── example-app/
    ├── manifest.yaml
    ├── icon.svg
    ├── locales/
    │   └── en.json
    ├── lawn-compose/
    │   └── compose.yaml
    └── init-files/
        └── custom.config.php

manifest.yaml

The manifest defines how the app appears in the catalog.

manifest.yaml
id: example-app
name: Example App
tagline: A full-featured example template
description: |
  An example app that demonstrates all Lawn template features.

  **Highlights:**
  Multi-service architecture with a PostgreSQL database,
  generated database credentials, and health monitoring.
symbolFallback: app.badge.checkmark
category: Utilities
compatibility:
  minLawnVersion: 0.2.0
tags:
  - example
  - demo
features:
  - User-provided admin password
  - PostgreSQL database with health checks
  - Configuration file injection
links:
  - url: https://github.com/example/example-app
    type: github
  - url: https://example.com/docs
    type: documentation
webPort: 8080

lawn-compose/compose.yaml

The compose file defines the services, variables, and Lawn extensions. Its display names, descriptions, and labels live in the localization file.

lawn-compose/compose.yaml
x-lawn:
  compatibility:
    minLawnVersion: 0.2.0
  config:
    POSTGRES_PASSWORD:
      source: generatedSecret
      format: alphanumeric
      length: 32
    SECRET_KEY:
      source: generatedSecret
      format: alphanumeric
      length: 50
    ADMIN_PASSWORD:
      source: userProvided
      required: true
    LOG_LEVEL:
      value: info
      weight: 10
      options:
        debug:
          compose:
            services:
              app:
                environment:
                  LOG_LEVEL: debug
        info:
          compose:
            services:
              app:
                environment:
                  LOG_LEVEL: info
        warning:
          compose:
            services:
              app:
                environment:
                  LOG_LEVEL: warning
    ENABLE_SIGNUP:
      inputType: boolean
      value: true
      weight: 20
      options:
        "true":
          compose:
            services:
              app:
                environment:
                  ENABLE_SIGNUP: "true"
        "false":
          compose:
            services:
              app:
                environment:
                  ENABLE_SIGNUP: "false"

services:
  app:
    image: example/app:1.0.0
    depends_on:
      - db
    environment:
      DATABASE_URL: "postgres://postgres:${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}@db:5432/app"
      SECRET_KEY: "${SECRET_KEY:?SECRET_KEY is required}"
      ADMIN_USER: admin
      ADMIN_PASSWORD: "${ADMIN_PASSWORD:?ADMIN_PASSWORD is required}"
      TZ: "${TIMEZONE:-UTC}"
      PUID: "${UID:-1000}"
      PGID: "${GID:-1000}"
    volumes:
      - "./data:/app/data"
      - "./config:/app/config"
    ports:
      - "8080:8080"
    deploy:
      resources:
        limits:
          memory: 1024M
    x-lawn:
      healthChecks:
        - id: web-ui
          type: http
          path: /health
          expectedStatus: 200
      notices:
        - id: login-credentials
          style: info
          elements:
            - id: username
              type: field
              value: admin
            - id: password
              type: field
              source: ADMIN_PASSWORD
              container: app
            - id: change-password
              type: text
      environment:
        ADMIN_USER:
        TZ:
        PUID:
        PGID:
      initFiles:
        - source: "custom.config.php"
          containerPath: "/app/config/custom.config.php"
      createDirectories:
        - "./data/uploads"
        - "./data/cache"

  db:
    image: postgres:15
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:?POSTGRES_PASSWORD is required}"
      PGDATA: /var/lib/postgresql/data/pgdata
    volumes:
      - "./postgres:/var/lib/postgresql/data"
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "postgres"]
      interval: 10s
      timeout: 5s
      retries: 5
    x-lawn:
      healthChecks:
        - id: db-ready
          type: command
          command: ["/bin/sh", "-c", "pg_isready -U postgres"]
      environment:
        POSTGRES_DB:
        POSTGRES_USER:
        PGDATA:

locales/en.json

The localization file provides configuration and extension strings. Keys match the variable names, health check IDs, notice IDs, and element IDs from the YAML.

locales/en.json
{
  "config": {
    "POSTGRES_PASSWORD": {
      "displayName": "Database Password",
      "description": "PostgreSQL superuser password. Auto-generated during install."
    },
    "SECRET_KEY": {
      "displayName": "Secret Key",
      "description": "Application secret key for session signing."
    },
    "ADMIN_PASSWORD": {
      "displayName": "Admin Password",
      "description": "Choose the admin account password in Edit Configuration before starting the app."
    },
    "LOG_LEVEL": {
      "displayName": "Log Level",
      "description": "Controls how much detail the application logs.",
      "options": {
        "debug": { "label": "Debug" },
        "info": { "label": "Info" },
        "warning": { "label": "Warning" }
      }
    },
    "ENABLE_SIGNUP": {
      "displayName": "Public Sign-up",
      "description": "Allow new users to create accounts.",
      "options": {
        "true": { "label": "On" },
        "false": { "label": "Off" }
      }
    }
  },
  "services": {
    "app": {
      "environment": {
        "ADMIN_USER": {
          "description": "Username for the admin account."
        },
        "TZ": {
          "description": "Timezone for date display and scheduling."
        },
        "PUID": {
          "description": "User ID for file ownership inside the container.",
          "group": "Permissions"
        },
        "PGID": {
          "description": "Group ID for file ownership inside the container.",
          "group": "Permissions"
        }
      },
      "healthChecks": {
        "web-ui": {
          "description": "Web UI is accessible and responding."
        }
      },
      "notices": {
        "login-credentials": {
          "title": "Login Credentials",
          "elements": {
            "username": { "label": "Username" },
            "password": { "label": "Password" },
            "change-password": { "content": "Change these in **Settings > Security** after your first login." }
          }
        }
      }
    },
    "db": {
      "environment": {
        "POSTGRES_DB": {
          "description": "Name of the PostgreSQL database."
        },
        "POSTGRES_USER": {
          "description": "PostgreSQL username."
        },
        "PGDATA": {
          "description": "Data directory inside the container. Do not change."
        }
      },
      "healthChecks": {
        "db-ready": {
          "description": "Database is accepting connections."
        }
      }
    }
  }
}

init-files/custom.config.php

Init files are copied into the container's volume mounts before each start. The containerPath must fall within a declared volume mount.

init-files/custom.config.php
<?php
// This file is injected by Lawn on every container start.
// It reads configuration from environment variables set by the compose file.

$config = [
    'database' => getenv('DATABASE_URL'),
    'timezone' => getenv('TZ') ?: 'UTC',
];

What Each Feature Does

FeatureWherePurpose
x-lawn.compatibilityTop-levelMinimum Lawn version required for this template
x-lawn.configTop-levelDeclares secrets, user-provided values, and config options
generatedSecretConfigRandom password created at install time
userProvidedConfigEditable value supplied through Edit Configuration after installation
required: trueConfigActive setting must have a nonempty value before startup
Config optionsConfigSelectable choices with compose overlays
inputType: booleanConfigToggle switch for on/off settings
weightConfigDisplay ordering in the settings UI
locales/en.jsonLocalizationDisplay names, descriptions, option labels, notice strings
System variablesEnvironmentTIMEZONE, UID, GID (always available)
healthChecksPer-serviceHTTP and command checks for readiness
noticesPer-serviceDismissible banners with credentials and guidance
environmentPer-serviceFormat hints for env vars
initFilesPer-serviceConfig files injected from template into volumes
createDirectoriesPer-serviceHost directories created before container start
deploy.resources.limitsPer-serviceMemory and CPU constraints

After installation, enter ADMIN_PASSWORD in Edit Configuration, save, then click Start. Its required: true declaration blocks startup until a value is supplied. Lawn generates POSTGRES_PASSWORD and SECRET_KEY; the login notice shows the admin password you chose.

Testing

The example/app:1.0.0 image and its URLs are illustrative. Replace them with your app's real image, endpoints, and configuration before trying to run this template.

Run the Compose syntax check with disposable variable values, then follow the local-catalog testing workflow. Install a disposable instance, complete Edit Configuration before Start, use the app, and verify that data persists after a stop and start. Remove the instance and local catalog registration when finished.

lawn template validate and lawn template test are planned and are not available in the current release.