Llomiriby hyprr

Install

Run lomiri with Docker Compose, with HTTPS set up for you.

lomiri ships as Docker images. The compose file below runs everything you need: Postgres, the API, the web UI and Caddy, which gets a TLS certificate for your domain automatically.

Requirements

  • A Linux server with Docker and Docker Compose 2.23 or newer.
  • A domain for the panel (for example panel.example.com) whose DNS points at the server.
  • Ports 80 and 443 reachable from the internet, so Caddy can issue the certificate.

Download the compose file

mkdir lomiri && cd lomiri
curl -fsSLO https://lomiri.hyprr.space/install/docker-compose.yml
curl -fsSL https://lomiri.hyprr.space/install/env.example -o .env

Fill in .env

Set the panel's public URL and generate the two secrets:

sed -i "s#^APP_URL=.*#APP_URL=https://panel.example.com#" .env
sed -i "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$(openssl rand -hex 24)/" .env
sed -i "s/^OAUTH_STATE_SECRET=.*/OAUTH_STATE_SECRET=$(openssl rand -hex 32)/" .env

Or open .env in an editor. Every option is described in Configuration.

Start lomiri

docker compose up -d

The first start pulls the images and creates the database. Later starts apply any database updates automatically.

Create your admin account

docker compose exec api bun run create:admin you@example.com 'a-strong-password' admin

The arguments are email, password and username.

Sign in

Open https://panel.example.com and sign in with the account you just created. From there, connect your first node and create a server.

The files

# lomiri — Postgres, API, web UI and Caddy (automatic HTTPS) in one stack.
#
#   1. Put this file and .env (from env.example) in an empty directory, fill in .env
#   2. docker compose up -d
#   3. docker compose exec api bun run create:admin you@example.com 'a-strong-password' admin
#
# Docs: https://panel.hyprr.space/docs
services:
  postgres:
    image: postgres:17-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: lomiri
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
      POSTGRES_DB: lomiri
    volumes:
      - postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U lomiri -d lomiri"]
      interval: 5s
      timeout: 5s
      retries: 10

  # Runs database migrations on start, then serves the API. Raise scale to run more replicas; Caddy
  # spreads requests across all of them.
  api:
    image: ghcr.io/hyprrspace/lomiri-api:${LOMIRI_TAG:-latest}
    restart: unless-stopped
    scale: 1
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      APP_URL: ${APP_URL:?set APP_URL in .env}
      APP_NAME: ${APP_NAME:-}
      DATABASE_URL: postgres://lomiri:${POSTGRES_PASSWORD}@postgres:5432/lomiri
      OAUTH_STATE_SECRET: ${OAUTH_STATE_SECRET:?set OAUTH_STATE_SECRET in .env}
      LOG_LEVEL: ${LOG_LEVEL:-info}
      TRUSTED_PROXIES: ${TRUSTED_PROXIES:-}

  web:
    image: ghcr.io/hyprrspace/lomiri-web:${LOMIRI_TAG:-latest}
    restart: unless-stopped
    scale: 1
    depends_on:
      - api
    environment:
      LOMIRI_API_URL: http://api:3000
      TRUSTED_PROXIES: ${TRUSTED_PROXIES:-}

  # Terminates TLS for APP_URL (certificates are issued automatically) and routes /api/* to the API,
  # everything else to the web UI. Remove it if you already run your own reverse proxy.
  caddy:
    image: caddy:2-alpine
    restart: unless-stopped
    depends_on:
      - api
      - web
    ports:
      - "80:80"
      - "443:443"
      - "443:443/udp"
    environment:
      APP_URL: ${APP_URL}
    configs:
      - source: caddyfile
        target: /etc/caddy/Caddyfile
    volumes:
      - caddy-data:/data
      - caddy-config:/config

configs:
  caddyfile:
    content: |
      {$$APP_URL} {
        encode zstd gzip
        # "dynamic a" re-resolves the service name, so every replica gets traffic.
        handle /api/* {
          reverse_proxy {
            dynamic a api 3000
          }
        }
        handle {
          reverse_proxy {
            dynamic a web 3000
          }
        }
      }

volumes:
  postgres:
  caddy-data:
  caddy-config:

On this page