Self-hosting Umami with Docker Compose

Umami is an open-source, privacy oriented and lightweight web analytics service written in Node. Umami is super easy and quick to self-host, and in this post we'll see a setup with Docker Compose.

The setup described in this post assumes that we already:

  • Have a website to monitor.
  • Have a domain to expose our Umami instance on.
  • Have Docker and Compose installed and operational.

The domain configuration is not covered here since you can do it as you prefer, but in the setup presented here, it is handled by these images:

The images that make up the Umami stack instead are (adapt versions as needed):

Umami also supports MySQL: the configuration is almost identical: just change images and configuration parameters appropriately.

Umami setup with Docker Compose


We want to create this structure:

├── .env
├── docker-compose.yml
└── sql/
    └── schema.postgresql.sql

So in the umami directory we can just execute:

mkdir sql
cd sql

The file schema.postgresql.sql is needed to execute the initial database migrations to create the schema, and it is available in Umami’s GitHub repository.

The docker-compose.yml file is also in the repository as an example, and it is quite perfect to start our configuration; in facts the setup that you see below is just a minor modification of it.


The Docker Compose that we want is:

version: '3'

    container_name: umami
    env_file: .env
      TRACKER_SCRIPT_NAME: saporito
      VIRTUAL_HOST: stats.ourdomain.ext
      LETSENCRYPT_HOST: stats.ourdomain.ext
      LETSENCRYPT_EMAIL: ouremail@ourdomain.ext
      - default
      - proxy
      - db
    restart: always

    container_name: umami_db
    image: postgres:12-alpine
    env_file: .env
      - ./sql/schema.postgresql.sql:/docker-entrypoint-initdb.d/schema.postgresql.sql:ro
      - umami-db-data:/var/lib/postgresql/data
      - default
    restart: always


      name: nginx-proxy

Some comments:

  • Umami is only exposed to localhost, because from the outside we want to reach it only through the proxy, not directly via port number.
  • The environment variables VIRTUAL_HOST, LETSENCRYPT_* are needed for the proxy, hence not strictly related to Umami.
  • TRACKER_SCRIPT_NAME allows to change the filename of the tracking script to avoid being blocked by some ad blockers; I have chosen “saporito” because it is the Italian translation of “umami”, which means “tasty”. With this configuration the tracking script will be like https://stats.ourdomain.ext/saporito.js instead of https://stats.ourdomain.ext/umami.js. This setting is optional.
  • To communicate with each other, umami and db are part of the same default network created by compose.
  • umami is also part of the proxy network created externally.
  • The database schema file is bind-mounted in /docker-entrypoint-initdb.d/, meaning that it will be executed as SQL query when Postgres starts.
  • The database data is persistently stored in the volume umami-db-data.


In the .env file we can place some environment variables related to the database credentials.



Some notes:

  • The obvious: replace the values with the real ones.
  • If you update the credentials, don’t forget to also update the DSN specified in DATABASE_URL.

Running the stack

At this point we can just spin up our containers and contextually see the logs:

docker-compose up -d && docker-compose logs -f

When the logs suggest us that the database has been migrated and Umami is ready, we can head up to https://stats.oursite.ext and login with the default credentials admin:umami.

To exit from the log tail, CTRL+C.


The next steps are really trivial and simple:

  1. Change the Umami credentials.
  2. Add the site in Umami.
  3. Get a tracking code.
  4. Place the tracking code in the site.

Now as soon as we navigate in any page that contains the tracking code, we should instantly see our visit in the real-time view in Umami.

Final thoughts

I’m positively surprised at how simple has been Umami to set up and get fully running. Previously I have been self-hosting Matomo, which in contrast feels bloated and slow to get up. Umami is like a breath of clean and fresh air.

Thanks for the read, bye 👋

Related Posts