Home AssistantHome AssistantContributors
On this page

The fastest way to understand apps is to build one. In this tutorial, you write a tiny app, install it on your own Home Assistant, and then extend it step by step. It takes about half an hour.

You need a Home Assistant installation that runs apps, which means Home Assistant OS, and a way to put files on it.

1. Get access to the local apps folder

The Supervisor looks for apps you build yourself in a folder called local_apps. Every app in there shows up in the app store under Local apps. To get to that folder, install one of these apps first:

  • Samba share: your Home Assistant shows up as a network share on your computer, with a folder called local_apps. On macOS, if it does not show up, open Finder, press Cmd + K, and enter smb://homeassistant.local.
  • Terminal & SSH: open its web terminal, or log in over SSH, and work in /local_apps. For SSH, add your public key to the app configuration first, see its documentation.

2. Create the app

In local_apps, create a folder called hello_world with three files: Dockerfile, config.yaml, and run.sh.

The Dockerfile describes the container image of your app. It starts from the Home Assistant base image, which already contains the tools most apps need:

Dockerfile
FROM ghcr.io/home-assistant/base:3.24

# Copy the start script into the image
COPY run.sh /
RUN chmod a+x /run.sh

CMD [ "/run.sh" ]

The config.yaml tells the Supervisor what your app is and what it needs:

YAML
name: "Hello world"
description: "My first real app!"
version: "1.0.0"
slug: "hello_world"
init: false
arch:
  - aarch64
  - amd64

The run.sh script runs when your app starts:

Shell
#!/command/with-contenv bashio

bashio::log.info "Hello world!"
Important

Save run.sh with Unix line endings (LF), not Windows line endings (CRLF). With CRLF, the script fails to start with an error that does not mention line endings at all.

A few things worth knowing about these files:

  • slug is the ID of your app. It must be unique within the repository, and it is good practice to give the folder the same name.
  • init: false is needed because the base image brings its own init system, S6 overlay. Run services with S6 overlay explains why.
  • The first line of run.sh loads Bashio, a library of shell functions for apps, with the environment of the container. bashio::log.info writes a log line with a time and a level. In step 6, you use Bashio to read options too.

3. Install and start your app

  1. In Home Assistant, go to Settings > Apps and select Install app. This opens the App store.
  2. In the top-right menu, select Check for updates. Refresh the page if needed.
  3. A new section, Local apps, shows up with your app in it. Select it.
  4. Select Install. The Supervisor now builds the image from your Dockerfile, which takes a moment.
  5. Select Start, then open the Log tab. You see a line like [12:34:56] INFO: Hello world!.

My app does not show up

First, refresh your browser with Ctrl + F5 (Windows and Linux) or Cmd + Shift + R (macOS). If it is still missing, the Supervisor could not read your config.yaml. It is either invalid YAML, or one of the keys has a value the Supervisor does not accept.

The Supervisor logs tell you which one. Go to Settings > System > Logs, select Supervisor in the menu at the top right, and look for a line that mentions your app. Fix the problem, then select Check for updates in the app store again.

4. Serve a web page

An app that prints one line is not very useful yet. Let's turn it into a web server, using the HTTP server that comes with Python.

Install Python in the Dockerfile, and make /data the working directory. /data is the persistent storage of your app; more on that in a moment.

Dockerfile
FROM ghcr.io/home-assistant/base:3.24

# Install the requirements of the app
RUN apk add --no-cache python3

# The Python HTTP server serves the working directory
WORKDIR /data

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

CMD [ "/run.sh" ]

Start the server in run.sh:

Shell
#!/command/with-contenv bashio

bashio::log.info "Hello world!"

python3 -m http.server 8000

Then expose port 8000 of the container on port 8000 of the host, and raise the version:

YAML
name: "Hello world"
description: "My first real app!"
version: "1.1.0"
slug: "hello_world"
init: false
arch:
  - aarch64
  - amd64
startup: services
ports:
  8000/tcp: 8000

5. Install the update

Because you raised version, Home Assistant offers an update for your app. Select Check for updates in the app store if you do not see it yet, then update the app. The Supervisor rebuilds the image with your changes. Did you forget to raise the version? Then uninstall and install the app again.

Start the app and open http://homeassistant.local:8000 in your browser. You see a file listing with one file in it: options.json.

6. Add options

options.json holds the settings the user chose for your app. The Supervisor writes it to /data before your app starts. It is empty now, because your app has no options yet. Add some to config.yaml: options holds the default values, and schema says what type each option is.

YAML
options:
  name: "world"
  beer: true
  year: 2017
schema:
  name: str
  beer: bool
  year: int

Raise the version again and update the app. The Configuration tab of your app now shows a form with these three options. Change one, save, and restart the app. Download options.json from your web server, and you see your values in it.

Your script can read the options with Bashio. Change run.sh:

Shell
#!/command/with-contenv bashio

name=$(bashio::config 'name')
bashio::log.info "Hello ${name}!"

if bashio::config.true 'beer'; then
  bashio::log.info "Cheers, it is $(bashio::config 'year')."
fi

python3 -m http.server 8000

Raise the version, update, and start the app again. The log now greets the name you set. bashio::config gets the options from the Supervisor, which is one reason the script starts with with-contenv: that gives it the token it needs to ask. The Bashio options reference has every function for options.

What is next

You now have the basics: a Dockerfile, a config.yaml, options, logging, and a port.

Next: build a real app. It walks through a complete example app, with services that start in order, options and logging with Bashio, translations, documentation, CI, and releases. That is how you set up an app other people install.

After that, how apps work explains what the Supervisor does with your app.

Other places to go from here:

  • Show your web server inside the Home Assistant UI with ingress, instead of opening a port.
  • Copy the example app repository as the start of your own. It has a build workflow, documentation files, and an AppArmor profile ready to go.
  • Look up every key in the app configuration reference.
  • Ready to help with the official apps or the tooling behind them? Continue with contribute to the official apps.
Was this page helpful?

Last updated on .