home-assistant-apps

Develop, configure, test, publish, and secure Home Assistant apps (formerly add-ons) — Docker-based extensions managed by the Supervisor. Use when creating new apps, writing Dockerfiles, configuring config.yaml, setting up Ingress, building repositories, implementing security best practices, or publishing to container registries.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "home-assistant-apps" with this command: npx skills add borales/home-assistant-skills/borales-home-assistant-skills-home-assistant-apps

Home Assistant Apps Development

Develop, configure, test, publish, and secure Home Assistant apps (formerly known as add-ons). Apps are Docker container images managed by the Home Assistant Supervisor that extend functionality — from MQTT brokers to file-sharing services and custom web UIs.

When to Use This Skill

  • Creating a new Home Assistant app from scratch
  • Writing or reviewing app config.yaml configuration
  • Creating or modifying app Dockerfile and build configuration
  • Setting up build.yaml for multi-architecture builds
  • Configuring app options and option schemas
  • Implementing inter-app communication (Supervisor API, Home Assistant API, services)
  • Setting up Ingress for embedded web UIs
  • Writing AppArmor security profiles
  • Creating app repositories with repository.yaml
  • Publishing apps to container registries (GHCR, Docker Hub)
  • Testing apps locally with devcontainers or Docker
  • Troubleshooting app installation, runtime, or build issues
  • Adding translations, documentation, changelogs, icons, and logos

Core Concepts

What Are Home Assistant Apps?

Apps (formerly called "add-ons") allow users to extend Home Assistant by running additional containerized applications alongside it. Under the hood, apps are Docker container images published to a container registry (e.g., GitHub Container Registry, Docker Hub). The Supervisor manages the full lifecycle: installation, configuration, starting, stopping, updating, and backups.

Developers create GitHub repositories containing one or more apps for easy community sharing.

Architecture Overview

┌──────────────────────────────────────────────┐
│              Home Assistant OS               │
│  ┌────────────────────────────────────────┐  │
│  │            Supervisor                  │  │
│  │  ┌──────────┐ ┌──────────┐ ┌────────┐  │  │
│  │  │  HA Core │ │  App A   │ │ App B  │  │  │
│  │  │(container│ │(container│ │(contai-│  │  │
│  │  │  image)  │ │  image)  │ │ner img)│  │  │
│  │  └──────────┘ └──────────┘ └────────┘  │  │
│  │       Internal Docker Network          │  │
│  └────────────────────────────────────────┘  │
│               Host OS / HW                   │
└──────────────────────────────────────────────┘
  • Supervisor orchestrates all containers, manages the internal network, and exposes management APIs.
  • Each app runs as an isolated Docker container on a shared internal network.
  • Apps communicate with HA Core and each other via the internal network using hostnames.
  • Apps can expose ports, mount host directories, and access hardware devices.

App File Structure

Every app lives in its own directory with a standard layout:

addon_name/
  translations/
    en.yaml
  apparmor.txt        # Optional: custom AppArmor profile
  build.yaml          # Optional: extended build configuration
  CHANGELOG.md        # Changelog for users
  config.yaml         # Required: app configuration
  DOCS.md             # User-facing documentation
  Dockerfile          # Required: container image definition
  icon.png            # App icon (128x128, square, PNG)
  logo.png            # App logo (~250x100, PNG)
  README.md           # Intro shown in app store
  run.sh              # Entry point script

Note: Translation files, config and build all support .json, .yml, and .yaml as file types.

Important: Avoid using config.yaml as a filename for anything other than the app configuration. The Supervisor searches recursively for config.yaml in the repository.

App Configuration (config.yaml)

The config.yaml is the heart of your app. It tells the Supervisor what the app does, what it needs, and how to present it.

Required Options

KeyTypeDescription
namestringDisplay name of the app
versionstringVersion of the app (must match Docker image tag if using image)
slugstringUnique, URI-friendly identifier within the repository scope
descriptionstringShort description of the app
archlistSupported architectures: armhf, armv7, aarch64, amd64, i386

Key Optional Options

KeyTypeDefaultDescription
urlurlHomepage/docs URL for the app
startupstringapplicationStart order: initializesystemservicesapplicationonce
bootstringautoauto, manual, or manual_only
portsdictNetwork ports: "container-port/type": host-port. Use null to disable
ports_descriptiondictHuman-readable port descriptions
host_networkboolfalseRun on the host network
maplistBind-mount HA directories: homeassistant_config, addon_config, ssl, addons, backup, share, media, all_addon_configs, data
optionsdictDefault option values
schemadictValidation schema for user options. Set to false to disable validation
imagestringPre-built container image name (e.g., ghcr.io/user/{arch}-addon-name)
ingressboolfalseEnable Ingress (embedded web UI via HA)
ingress_portint8099Internal port for Ingress
ingress_entrystring/URL entry point for Ingress
ingress_streamboolfalseEnable request streaming for Ingress
homeassistant_apiboolfalseEnable access to HA Core REST API via proxy
hassio_apiboolfalseEnable access to Supervisor REST API
hassio_rolestringdefaultAPI role: default, homeassistant, backup, manager, admin
auth_apiboolfalseAllow access to HA user authentication backend
privilegedlistKernel capabilities: NET_ADMIN, SYS_ADMIN, SYS_RAWIO, etc.
full_accessboolfalseFull hardware access (use sparingly!)
apparmorbool/stringtrueEnable or specify custom AppArmor profile
deviceslistHost device paths to map (e.g., /dev/ttyAMA0)
uartboolfalseAuto-map all UART/serial devices
usbboolfalseMap raw USB access with plug & play
gpioboolfalseMap GPIO interface
audioboolfalseUse internal PulseAudio system
videoboolfalseMap all video devices
homeassistantstringMinimum required HA Core version (e.g., 2024.10.5)
initbooltrueUse Docker default init. Set false for S6 Overlay v3+
stdinboolfalseEnable STDIN via HA API
backupstringhothot or cold (cold stops the app first)
backup_prestringCommand to run before backup
backup_poststringCommand to run after backup
backup_excludelistGlob patterns to exclude from backups
watchdogstringHealth monitoring URL
advancedboolfalseOnly show to users with "Advanced" mode enabled
stagestringstablestable, experimental, or deprecated
timeoutint10Seconds before Docker daemon kill
tmpfsboolfalseUse tmpfs for /tmp
discoverylistServices this app provides for HA discovery
serviceslistService dependencies: service:provide, service:want, service:need
breaking_versionslistVersions requiring manual update
panel_iconstringmdi:puzzleMDI icon for sidebar panel
panel_titlestringCustom sidebar panel title
panel_adminbooltrueRestrict panel to admin users
webuistringExternal web UI URL template
environmentdictEnvironment variables for the container
host_dbusboolfalseMap host D-Bus into the app
host_pidboolfalseShare host PID namespace (not protected only)
host_ipcboolfalseShare IPC namespace
host_utsboolfalseUse host UTS namespace
kernel_modulesboolfalseMap host kernel modules (read-only)
realtimeboolfalseHost schedule access with SYS_NICE
journaldboolfalseMap host system journal (read-only)
udevboolfalseMount host udev database (read-only)
devicetreeboolfalseMap /device-tree
legacyboolfalseEnable legacy mode for images without hass.io labels
ulimitsdictResource limits (e.g., nofile) as int or {soft, hard}

Full Configuration Example

name: "My Smart App"
version: "2.1.0"
slug: "my_smart_app"
description: "A feature-rich app for Home Assistant"
url: "https://github.com/user/ha-my-smart-app"
arch:
  - aarch64
  - amd64
  - armv7
startup: services
boot: auto
ports:
  8080/tcp: 8080
  1883/tcp: null
ports_description:
  8080/tcp: "Web interface"
  1883/tcp: "MQTT (disabled by default)"
map:
  - type: homeassistant_config
    read_only: true
  - type: ssl
  - type: share
    read_only: false
homeassistant: "2024.1.0"
homeassistant_api: true
hassio_api: true
hassio_role: default
ingress: true
ingress_port: 8080
auth_api: true
init: false
panel_icon: mdi:robot
panel_title: "Smart App"
watchdog: "http://[HOST]:[PORT:8080]/health"
options:
  mqtt_host: ""
  mqtt_port: 1883
  log_level: "info"
schema:
  mqtt_host: str
  mqtt_port: port
  log_level: "list(debug|info|warning|error)"
image: "ghcr.io/user/{arch}-my-smart-app"

Options & Schema Validation

The options key defines defaults; schema defines validation rules. Set a default to null to make an option mandatory (user must provide a value before the app starts).

Supported schema types:

TypeVariantsDescription
strstr(min,), str(,max), str(min,max)String with optional length constraints
boolBoolean true/false
intint(min,), int(,max), int(min,max)Integer with optional range
floatfloat(min,), float(,max), float(min,max)Floating point with optional range
emailValid email address
urlValid URL
passwordPassword field (masked in UI)
portValid network port number
match(REGEX)Match against a regular expression
list(val1|val2|...)Enumerated list of allowed values
devicedevice(subsystem=tty)Device path with optional filter

Making an option truly optional (no default, not required): use ? suffix in the schema and omit from options:

options:
  required_name: "World"
  required_port: 8080
schema:
  required_name: str
  required_port: port
  optional_debug: "bool?" # Truly optional — no default, not required
  optional_label: "str?" # Truly optional

Nested structures (max depth 2):

options:
  logins:
    - username: admin
      password: "secret"
schema:
  logins:
    - username: str
      password: password

Removing deprecated options programmatically (Bashio):

options=$(bashio::addon.options)
old_key='deprecated_option'
if bashio::jq.exists "${options}" ".${old_key}"; then
    bashio::log.info "Removing ${old_key}"
    bashio::addon.option "${old_key}"
fi

App Dockerfile

All apps are based on Alpine Linux base images. Home Assistant automatically substitutes the correct base image per architecture.

Standard Dockerfile Template

ARG BUILD_FROM
FROM $BUILD_FROM

# Install requirements for app
RUN \
  apk add --no-cache \
    python3 \
    py3-pip

# Copy data for app
COPY run.sh /
RUN chmod a+x /run.sh

CMD [ "/run.sh" ]

Build Arguments

ArgDescription
BUILD_FROMDynamic base image for the target architecture
BUILD_VERSIONApp version read from config.yaml
BUILD_ARCHCurrent build architecture

Architecture-Specific Dockerfiles

You can suffix the Dockerfile for architecture-specific builds:

Dockerfile          # Default
Dockerfile.amd64    # AMD64-specific
Dockerfile.aarch64  # ARM64-specific

Required Labels (for non-local builds)

LABEL \
  io.hass.version="VERSION" \
  io.hass.type="addon" \
  io.hass.arch="armhf|aarch64|i386|amd64"

App Script (run.sh)

The entry point script runs when the container starts. All HA app base images come with Bashio pre-installed for common operations.

Reading Options

#!/usr/bin/with-contenv bashio

# Read options from /data/options.json via Bashio
TARGET=$(bashio::config 'target')
LOG_LEVEL=$(bashio::config 'log_level')

bashio::log.info "Starting with target=${TARGET}, log_level=${LOG_LEVEL}"

Key Runtime Paths

PathDescription
/dataPersistent storage volume
/data/options.jsonUser configuration (JSON)
/configMapped addon_config directory (if configured)
/shareShared HA files (if mapped)
/sslSSL certificates (if mapped)
/homeassistantHA config directory (if mapped)

Extended Build Configuration (build.yaml)

Use build.yaml for custom base images, extra build args, labels, and code signing:

build_from:
  aarch64: mycustom/base-image:latest
  amd64: mycustom/amd64-image:latest
args:
  MY_BUILD_ARG: xy
labels:
  org.opencontainers.image.title: "My App"
codenotary:
  signer: dev@example.com
  base_image: notary@home-assistant.io
KeyRequiredDescription
build_fromnoArchitecture → base image mapping
argsnoAdditional Docker build arguments
labelsnoAdditional Docker labels
codenotarynoCodenotary CAS signing configuration
codenotary.signernoSigner email for image verification
codenotary.base_imagenoEmail to verify base image (use notary@home-assistant.io for official images)

App Communication

Internal Network

All apps share an internal Docker network and can communicate using hostnames:

  • Name format: {REPO}_{SLUG} (e.g., local_my_addon, a1b2c3_my_addon)
  • DNS hostname: Replace _ with - (e.g., local-my-addon)
  • Apps on host network can reach internal apps by name, but not vice versa — use aliases for bidirectional access.
  • Use supervisor as the hostname for the Supervisor API.

Communicating with Home Assistant Core

Enable homeassistant_api: true in config.yaml, then use the internal proxy:

#!/usr/bin/with-contenv bashio

# REST API via internal proxy (auto-authenticated)
curl -X GET \
  -H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
  -H "Content-Type: application/json" \
  http://supervisor/core/api/config

# WebSocket API
# Connect to: ws://supervisor/core/websocket
# Use SUPERVISOR_TOKEN as password
  • The SUPERVISOR_TOKEN environment variable is automatically injected.
  • REST endpoint: http://supervisor/core/api/
  • WebSocket endpoint: ws://supervisor/core/websocket

Communicating with the Supervisor API

Enable hassio_api: true and optionally set hassio_role:

#!/usr/bin/with-contenv bashio

# Get Supervisor info
curl -X GET \
  -H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
  http://supervisor/info

API calls available without hassio_api: true:

  • /core/api, /core/api/stream, /core/websocket
  • /addons/self/*
  • /services*, /discovery*
  • /info

Services API (Inter-App Service Discovery)

Apps can discover shared services (MQTT, MySQL) without user configuration:

#!/usr/bin/with-contenv bashio

# Discover MQTT service
MQTT_HOST=$(bashio::services mqtt "host")
MQTT_USER=$(bashio::services mqtt "username")
MQTT_PASSWORD=$(bashio::services mqtt "password")

# Use in your app
mosquitto_sub -h "${MQTT_HOST}" -u "${MQTT_USER}" -P "${MQTT_PASSWORD}" -t "#"

Declare service usage in config.yaml:

services:
  - mqtt:want # Can use MQTT if available
  - mysql:need # Requires MySQL to function

Service relationship types:

  • provide — this app provides the service
  • want — this app can optionally use the service
  • need — this app requires the service to work

Ingress (Embedded Web UI)

Ingress allows users to access your app's web interface directly through the Home Assistant UI, with authentication handled automatically by HA.

Ingress Configuration

# config.yaml
ingress: true
ingress_port: 8099 # Default; change if your server uses a different port
ingress_entry: / # URL entry point

Ingress Requirements

  1. Set ingress: true in config.yaml
  2. Your web server must listen on the configured ingress_port (default 8099)
  3. Only allow connections from 172.30.32.2 — deny all other IPs
  4. Users are pre-authenticated by HA — no additional auth needed

Ingress Supports

  • HTTP/1.x
  • Streaming content
  • WebSockets

Nginx Ingress Example

ingress.conf:

server {
    listen 8099;
    allow  172.30.32.2;
    deny   all;
}

Dockerfile:

ARG BUILD_FROM
FROM $BUILD_FROM

RUN \
  apk --no-cache add \
    nginx \
  && mkdir -p /run/nginx

COPY ingress.conf /etc/nginx/http.d/

CMD [ "nginx", "-g", "daemon off;error_log /dev/stdout debug;" ]

config.yaml:

name: "Ingress Example"
version: "1.0.0"
slug: "nginx-ingress-example"
description: "Ingress testing"
arch:
  - amd64
  - armhf
  - armv7
  - i386
ingress: true

User Identification via Ingress Headers

When accessed via Ingress, the Supervisor injects user identity headers:

HeaderDescription
X-Remote-User-IdAuthenticated HA user ID
X-Remote-User-NameUsername
X-Remote-User-Display-NameDisplay name

App Translations

Provide localized configuration labels in translations/{language_code}.yaml:

# translations/en.yaml
configuration:
  ssl:
    name: Enable SSL
    description: Enable usage of SSL on the webserver inside the app
  ssh:
    name: SSH Options
    description: Configure SSH authentication options
    fields:
      public_key:
        name: Public Key
        description: Client Public Key
      private_key:
        name: Private Key
        description: Client Private Key

network:
  80/TCP: The webserver port (Not used for Ingress)

App Repositories

Repository Structure

A repository is a Git repo containing one or more app directories plus a repository.yaml at the root:

my-ha-apps/
  repository.yaml
  addon_a/
    config.yaml
    Dockerfile
    ...
  addon_b/
    config.yaml
    Dockerfile
    ...

Repository Configuration (repository.yaml)

name: My Home Assistant Apps
url: https://github.com/user/my-ha-apps
maintainer: Your Name <email@example.com>
KeyRequiredDescription
nameyesRepository display name
urlnoRepository homepage
maintainernoContact info

Installing a Repository

Users add repositories via: Settings → Apps → App Store → ⋮ → Repositories

You can provide a my.home-assistant.io link for one-click installation.

Stable and Canary Branches

Offer multiple release channels using Git branches:

https://github.com/user/my-ha-apps          # stable
https://github.com/user/my-ha-apps#beta     # beta channel
https://github.com/user/my-ha-apps#next     # canary/next channel

Use different repository names per branch (e.g., "My App (stable)" vs. "My App (beta)").

Testing

Recommended: VS Code Devcontainer

The fastest development workflow uses the official devcontainer:

  1. Install the Remote Containers VS Code extension
  2. Copy devcontainer.json to .devcontainer/devcontainer.json
  3. Copy tasks.json to .vscode/tasks.json
  4. Open in VS Code → "Reopen in Container"
  5. Run task: Terminal → Run Task → "Start Home Assistant"
  6. Access HA at http://localhost:7123/
  7. Your apps appear automatically under Local Apps

Remote Development

For hardware-dependent apps, copy files to a real device's /addons directory via Samba or SSH. Comment out the image key in config.yaml to force local builds:

# image: ghcr.io/user/{arch}-my-addon   # Comment out for local build

Local Docker Build

Using the official builder:

docker run \
  --rm -it --name builder --privileged \
  -v /path/to/addon:/data \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  ghcr.io/home-assistant/amd64-builder \
  -t /data --all --test \
  -i my-test-addon-{arch} -d local

Using standalone Docker:

docker build \
  --build-arg BUILD_FROM="ghcr.io/home-assistant/amd64-base:latest" \
  -t local/my-test-addon \
  .

Local Docker Run

docker run \
  --rm \
  -v /tmp/my_test_data:/data \
  -p 8080:8080 \
  local/my-test-addon

Base Images

ArchitectureBase Image
amd64ghcr.io/home-assistant/amd64-base:latest
aarch64ghcr.io/home-assistant/aarch64-base:latest
armhfghcr.io/home-assistant/armhf-base:latest
armv7ghcr.io/home-assistant/armv7-base:latest
i386ghcr.io/home-assistant/i386-base:latest

Logs

All stdout and stderr output goes to Docker logs, viewable from the app page in the Supervisor panel.

Publishing

Pre-Built Containers (Recommended)

Build images for each architecture and push to a container registry. This provides fast, reliable installs for users.

Using the official builder (from a Git repo):

docker run \
  --rm --privileged \
  -v ~/.docker/config.json:/root/.docker/config.json:ro \
  ghcr.io/home-assistant/amd64-builder \
  --all \
  -t addon-folder \
  -r https://github.com/user/addons \
  -b main

Using the official builder (local directory):

docker run \
  --rm --privileged \
  -v ~/.docker/config.json:/root/.docker/config.json:ro \
  -v /my_addon:/data \
  ghcr.io/home-assistant/amd64-builder \
  --all \
  -t /data

Image naming with {arch} substitution in config.yaml:

image: "ghcr.io/user/{arch}-addon-name"

The {arch} placeholder is replaced with the user's architecture at install time.

Workflow tip: Use a separate build branch or PR for building. After pushing images, merge to main. The default branch should always match the latest container registry tag.

Locally Built Containers

Apps without the image key will be built on the user's device. This is simpler for development but creates slower installs and more SD card wear. Migrate to pre-built containers once your app is established.

Presentation Best Practices

Documentation Files

FilePurpose
README.mdShort intro shown in the app store
DOCS.mdDetailed user documentation (configuration, troubleshooting, license)
CHANGELOG.mdVersion history following Keep a Changelog

Icons and Logos

AssetFileFormatSize
Iconicon.pngPNG128×128px (1:1 square)
Logologo.pngPNG~250×100px

Security

Security Rating System

Every app starts with a base score of 5 (out of 6). Actions during development adjust the score:

ActionScore ImpactNotes
ingress: true+2Overrides auth_api rating
auth_api: true+1Overridden by Ingress
Codenotary CAS signed+1
Custom apparmor.txt+1Applied after installation
apparmor: false-1
privileged with NET_ADMIN, SYS_ADMIN, SYS_RAWIO, SYS_PTRACE, SYS_MODULE, or DAC_READ_SEARCH; or kernel_modules-1Applied once even if multiple
hassio_role: manager-1
host_network: true-1
hassio_role: admin-2
host_pid: true-2
host_uts: true + privileged: SYS_ADMIN-1
full_access: trueSet to 1Overrides all other adjustments
docker_api: trueSet to 1Overrides all other adjustments

API Roles

RoleAccess Level
defaultInfo-level API calls only
homeassistantAll Home Assistant API endpoints
backupAll backup API endpoints
managerExtended rights for CLI-type apps
adminFull API access; can toggle protection mode

Best Practices for Secure Apps

  1. Don't run on host network unless absolutely necessary
  2. Create a custom AppArmor profile (apparmor.txt) for a +1 security boost
  3. Map folders read-only unless write access is genuinely needed
  4. Minimize API permissions — use the lowest hassio_role that works
  5. Sign images with Codenotary CAS for a +1 security boost
  6. Use Ingress instead of exposing ports directly (+2 security boost)
  7. Use HA auth backend instead of custom credentials (auth_api: true)

AppArmor Profile Template

#include <tunables/global>

profile ADDON_SLUG flags=(attach_disconnected,mediate_deleted) {
  #include <abstractions/base>

  # Capabilities
  file,
  signal (send) set=(kill,term,int,hup,cont),

  # S6-Overlay
  /init ix,
  /bin/** ix,
  /usr/bin/** ix,
  /run/{s6,s6-rc*,service}/** ix,
  /package/** ix,
  /command/** ix,
  /etc/services.d/** rwix,
  /etc/cont-init.d/** rwix,
  /etc/cont-finish.d/** rwix,
  /run/{,**} rwk,
  /dev/tty rw,

  # Bashio
  /usr/lib/bashio/** ix,
  /tmp/** rwk,

  # App data
  /data/** rw,

  # Service profile
  /usr/bin/myprogram cx -> myprogram,

  profile myprogram flags=(attach_disconnected,mediate_deleted) {
    #include <abstractions/base>

    signal (receive) peer=*_ADDON_SLUG,

    /data/** rw,
    /share/** rw,

    /usr/bin/myprogram r,
    /bin/bash rix,
    /bin/echo ix,
    /etc/passwd r,
    /dev/tty rw,
  }
}

Steps to create a custom AppArmor profile:

  1. Start with minimum required access
  2. Add complain flag to the profile for testing
  3. Run the app and review audit log: journalctl _TRANSPORT="audit" -g 'apparmor="ALLOWED"'
  4. Add access rules for every allowed action
  5. Remove complain flag so violations are denied not just logged
  6. Repeat when updating the service

Protection Mode

All apps run in protection-enabled mode by default, restricting system access. Options like full_access, docker_api, and host_pid are only available when protection is disabled. Use with extreme caution.

Authenticating via HA User Backend

Instead of storing credentials in plain text config:

# config.yaml
auth_api: true

Then validate credentials against HA's auth backend via the Supervisor Auth API endpoint.

App Advanced Options (addon_config)

For apps that need user-provided files (custom configs, binary files, etc.):

  1. Add addon_config to map in config.yaml (use addon_config:rw if write access is needed)
  2. Users place files in /addon_configs/{REPO}_{SLUG}/
  3. Files are mounted at /config inside the container
  4. Provide an option in your schema for the file path, or document a fixed filename

Use cases:

  • Complex configuration files that an internal service reads directly
  • Binary files or certificates
  • Live-reloading config for internal services
  • Output files for user access (logs, databases, generated files)

Common Patterns

Pattern 1: Minimal "Hello World" App

config.yaml:

name: "Hello World"
description: "My first real app!"
version: "1.0.0"
slug: "hello_world"
init: false
arch:
  - aarch64
  - amd64
  - armhf
  - armv7
  - i386

Dockerfile:

ARG BUILD_FROM
FROM $BUILD_FROM

COPY run.sh /
RUN chmod a+x /run.sh

CMD [ "/run.sh" ]

run.sh:

#!/usr/bin/with-contenv bashio

echo "Hello world!"

Pattern 2: Python HTTP Server with Port Exposure

config.yaml:

name: "Hello World"
description: "My first real app!"
version: "1.1.0"
slug: "hello_world"
init: false
arch:
  - aarch64
  - amd64
  - armhf
  - armv7
  - i386
startup: services
ports:
  8000/tcp: 8000
options: {}
schema: {}

Dockerfile:

ARG BUILD_FROM
FROM $BUILD_FROM

RUN \
  apk add --no-cache \
    python3

WORKDIR /data

COPY run.sh /
RUN chmod a+x /run.sh

CMD [ "/run.sh" ]

run.sh:

#!/usr/bin/with-contenv bashio

echo "Hello world!"
python3 -m http.server 8000

Pattern 3: App with Options and HA API Access

config.yaml:

name: "Smart Monitor"
version: "1.0.0"
slug: "smart_monitor"
description: "Monitors entities and sends notifications"
arch:
  - aarch64
  - amd64
startup: application
homeassistant_api: true
ingress: true
ingress_port: 8099
init: false
options:
  entities: []
  refresh_interval: 30
schema:
  entities:
    - str
  refresh_interval: "int(5,3600)"

run.sh:

#!/usr/bin/with-contenv bashio

REFRESH=$(bashio::config 'refresh_interval')

bashio::log.info "Starting Smart Monitor (refresh every ${REFRESH}s)"

while true; do
  # Call HA API to get entity states
  STATES=$(curl -s \
    -H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
    -H "Content-Type: application/json" \
    http://supervisor/core/api/states)

  bashio::log.info "Fetched states successfully"
  sleep "${REFRESH}"
done

Pattern 4: Service Provider App (MQTT)

# config.yaml
name: "Custom MQTT Broker"
version: "2.0.0"
slug: "custom_mqtt"
description: "Custom MQTT broker with advanced features"
arch:
  - aarch64
  - amd64
startup: services
ports:
  1883/tcp: 1883
  9001/tcp: null
services:
  - mqtt:provide
discovery:
  - mqtt
options:
  persistence: true
  max_connections: 100
schema:
  persistence: bool
  max_connections: "int(1,10000)"

Pattern 5: GitHub Actions CI/CD for App Publishing

# .github/workflows/publish.yml
name: Publish App
on:
  release:
    types: [published]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          platforms: linux/amd64,linux/arm64,linux/arm/v7
          tags: |
            ghcr.io/${{ github.repository }}:${{ github.event.release.tag_name }}
            ghcr.io/${{ github.repository }}:latest

Troubleshooting

App Not Appearing in Store

  1. Press Ctrl+F5 (Windows/Linux) or Cmd+Shift+R (macOS) to clear browser cache
  2. Check Settings → System → Logs → Supervisor for validation errors
  3. Verify config.yaml is valid YAML (use yamllint.com)
  4. Ensure slug is unique and URI-friendly
  5. Click "Check for updates" in the App Store

App Won't Start

  1. Check Docker logs via the app's "Log" tab in the Supervisor panel
  2. Verify run.sh uses LF line endings (not CRLF)
  3. Ensure run.sh is executable (chmod a+x run.sh)
  4. If using S6 Overlay v3+, set init: false in config.yaml

Config Changes Not Taking Effect

  1. Bump the version in config.yaml
  2. Click "Check for updates" → Reinstall
  3. Or: uninstall and reinstall the app

References

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

Vuln Briefing

Generate daily vulnerability briefings from NIST NVD, CISA KEV, and security advisories. Aggregates, scores, and formats CVE data into actionable reports. No...

Registry SourceRecently Updated
Security

Cogdx

Cognitive diagnostics for AI agents. Calibration audits, bias detection, reasoning verification, and deception analysis. External verification you can't do y...

Registry SourceRecently Updated
Security

Sentinel — Agent Security Layer

Runtime security layer for OpenClaw agents. Intercepts and scans all external input (emails, API responses, web content, chat messages, calendar events) for...

Registry SourceRecently Updated
520Profile unavailable
Security

Ring Security

Monitor and manage Ring doorbells and security cameras. Query device status, review motion events, manage modes, and export event history. Use when you need...

Registry SourceRecently Updated
1780Profile unavailable