Skip to content

Applications & App Store

The App Store lets you deploy containerized workloads to any Kubernetes-enabled device in your fleet. Apps are defined as Kubernetes manifests paired with a metadata.yaml file, and can be sourced from the built-in library, your own Git repositories, or Helm charts.


How It Works

App Source (Git / Helm / Local)
  Repository Manager  ←── sync on schedule or manual trigger
    App Catalog  ←── browse, search, filter by tag
  Configure Fields  ←── fill in API keys, settings, secrets
  Template Substitution  ←── ${MY_FIELD} replaced in manifests
  kubectl apply  ──→  target device's K3s cluster

App Sources

Type Description
Local Built-in apps shipped with Watchgrid (in apps/ directory)
Git Any public or private Git repository
Helm Helm chart repositories

Adding an App Repository

Go to Applications → Repository Manager (gear icon in the top right).

Git Repository

Create a Git repository that follows the app directory layout, then add it to Watchgrid:

Field Example
Name my-apps
Type git
URL https://github.com/youruser/watchgrid-apps.git
Branch main
Username (optional, for private repos)
Password (optional, personal access token)
SSH Private Key (optional, PEM-encoded key contents)

For SSH-based access (git@host:org/repo.git), paste the contents of the PEM private key into the SSH Private Key field — Watchgrid stores it in the database and writes it to a temp file (mode 0600) only for the duration of each sync. The server uses it via GIT_SSH_COMMAND=ssh -i <tmp> -o IdentitiesOnly=yes -o StrictHostKeyChecking=no. Stored secrets (password and SSH key) are never returned by the API — repository listings expose has_password / has_ssh_key booleans instead.

Once added, Watchgrid clones the repo and scans it for apps. Use Sync to pull the latest changes at any time. The Sync All button fires every repository in parallel and surfaces per-repository status (Syncing…, Synced at HH:MM:SS, or Sync failed: <reason>) inline within each repository row.

Helm Repository

Field Example
Name bitnami
Type helm
URL https://charts.bitnami.com/bitnami
Chart nginx

App Directory Layout

Each app lives in its own subdirectory within the repository:

my-apps-repo/
  nginx-proxy/
    metadata.yaml       ← app definition and config fields
    deployment.yaml     ← Kubernetes Deployment + Service
  mqtt-broker/
    metadata.yaml
    deployment.yaml
    configmap.yaml      ← any number of manifest files
  grafana/
    metadata.yaml
    deployment.yaml
    pvc.yaml
    secret.yaml

All .yaml / .yml files except metadata.yaml are treated as deployable manifests and applied together on deploy.


Writing metadata.yaml

metadata.yaml defines how your app appears in the catalog and what configuration fields users fill in before deploying.

Minimal Example

name: "Nginx Proxy"
description: "A simple reverse proxy"
version: "1.0.0"
namespace: "default"
author: "Your Name"
tags:
  - "web"
  - "proxy"

Full Example with Config Fields

name: "MQTT Broker"
description: "Mosquitto MQTT broker for IoT devices"
version: "2.0.1"
namespace: "iot"
author: "Your Name"
tags:
  - "iot"
  - "mqtt"
  - "networking"
icon: "📡"
expose_port: 1883
expose_protocol: tcp
min_replicas: 1
max_replicas: 3
default_replicas: 1
config_fields:
  - name: "ADMIN_PASSWORD"
    type: "secret"
    description: "Broker admin password"
    required: true
  - name: "MAX_CONNECTIONS"
    type: "string"
    description: "Maximum concurrent client connections"
    default: "1000"
    required: false
  - name: "ALLOW_ANONYMOUS"
    type: "boolean"
    description: "Allow anonymous client connections"
    default: "false"
    required: false

Metadata Fields

Field Type Description
name string Display name in the app catalog
description string Short description shown in the catalog card
version string App version label
namespace string Default K8s namespace (default if omitted)
author string Informational — shown in app details
tags list Used for search and filtering
icon string Emoji shown in the catalog card
expose_port int Primary port the app listens on
expose_protocol string http, https, tcp, udp
min_replicas int Minimum replica count
max_replicas int Maximum replica count
default_replicas int Starting replica count
config_fields list Fields shown in the configure modal

config_fields Schema

Field Type Description
name string Key used in manifest template substitution
type string string, secret, or boolean
description string Help text shown in the UI
default string Pre-filled default value
required bool Blocks deployment if empty

UI rendering: - string → text input - secret → masked password input - boolean → true/false dropdown


Writing the Manifest

Kubernetes manifests are standard YAML. Use ${FIELD_NAME} placeholders anywhere you want user-provided values substituted.

Example: deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mqtt-broker
  labels:
    app: mqtt-broker
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mqtt-broker
  template:
    metadata:
      labels:
        app: mqtt-broker
    spec:
      containers:
      - name: mosquitto
        image: eclipse-mosquitto:2
        ports:
        - containerPort: 1883
        env:
        - name: ALLOW_ANONYMOUS
          value: "${ALLOW_ANONYMOUS}"
        - name: MAX_CONNECTIONS
          value: "${MAX_CONNECTIONS}"
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
          requests:
            memory: "64Mi"
            cpu: "100m"
---
apiVersion: v1
kind: Secret
metadata:
  name: mqtt-broker-secret
type: Opaque
stringData:
  ADMIN_PASSWORD: "${ADMIN_PASSWORD}"
---
apiVersion: v1
kind: Service
metadata:
  name: mqtt-broker-service
spec:
  selector:
    app: mqtt-broker
  ports:
    - name: mqtt
      protocol: TCP
      port: 1883
      targetPort: 1883
  type: ClusterIP

How Template Substitution Works

Before applying the manifest, Watchgrid replaces all ${FIELD_NAME} placeholders with the values the user entered in the configure modal.

Substitution happens in three ways:

  1. Direct string replacement${FIELD_NAME} anywhere in the manifest
  2. Environment variables- name: FIELD_NAME followed by value: is updated automatically
  3. Secret stringData — keys in stringData matching the field name are updated automatically

Configuration values are persisted per device — redeploying the same app on the same device reuses the saved configuration.


Deploying an App

  1. Select a device — only Kubernetes-enabled devices appear
  2. Select a namespace — discovered from the target device
  3. Browse the catalog — search by name or filter by tag
  4. Click Deploy on an app
  5. Fill in config fields — required fields must be completed
  6. Deploy or Install:
  7. Deploy — applies the manifest and starts the app immediately
  8. Install — downloads the app without starting it

Managing Deployed Apps

The deployed apps list shows per-device status with the following actions:

Action Description
Start Start a stopped app
Stop Scale replicas to zero (preserves config)
Uninstall Remove all Kubernetes resources for this app

Routines (Scheduling)

Routines let you schedule recurring actions on deployed apps.

Go to Applications → Routines and create a routine:

Field Description
Action start, stop, or restart
Schedule Cron expression — e.g. 0 2 * * * for 2:00 AM daily
Target Specific app on a specific device
Timezone Configurable per routine

Example: Restart your monitoring stack every night at 3:00 AM:

Action:   restart
Schedule: 0 3 * * *
Target:   my-device / prometheus-stack