Compare commits

..
Author SHA1 Message Date
ajnart a37e101bba fix(docker): normalize container CPU usage 2026-09-06 10:54:05 +02:00
5 changed files with 32 additions and 36 deletions

No files matched your search

+7 -9
View File
@@ -1,24 +1,22 @@
# Since .env is gitignored, you can use .env.example to build a new `.env` file when you clone the repo. # Since .env is gitignored, you can use .env.example to build a new `.env` file when you clone the repo.
# Keep this file up-to-date when you add new variables to `.env`. # Keep this file up-to-date when you add new variables to \`.env\`.
# This file will be committed to version control, so make sure not to have any secrets in it. # This file will be committed to version control, so make sure not to have any secrets in it.
# If you are cloning this repo, create a copy of this file named `.env` and populate it with your secrets. # If you are cloning this repo, create a copy of this file named `.env` and populate it with your secrets.
# Generate two different 64-character hex values with `openssl rand -hex 32` before starting Homarr. # The below secret is not used anywhere but required for Auth.js (Would encrypt JWTs and Mail hashes, both not used)
# Never reuse these values or commit the populated `.env` file. AUTH_SECRET="supersecret"
AUTH_SECRET=
# The below secret is used to encrypt integration secrets in the database. # The below secret is used to encrypt integration secrets in the database.
# Homarr will reject an empty or invalid value and print a randomly generated candidate. # It should be a 32-byte string, generated by running `openssl rand -hex 32` on Unix
SECRET_ENCRYPTION_KEY= # or starting the project without any (which will show a randomly generated one).
SECRET_ENCRYPTION_KEY=0000000000000000000000000000000000000000000000000000000000000000
LOG_LEVEL='info' LOG_LEVEL='info'
# This is how you can use the sqlite driver: # This is how you can use the sqlite driver:
DB_DRIVER='better-sqlite3' DB_DRIVER='better-sqlite3'
# Safe local source-development path. SQLite files are ignored by Git. DB_URL='FULL_PATH_TO_YOUR_SQLITE_DB_FILE'
# Container images use /appdata/db/db.sqlite by default instead.
DB_URL='./db.sqlite'
# Those are the two ways to use the mysql2 driver: # Those are the two ways to use the mysql2 driver:
# 1. Using the URL format: # 1. Using the URL format:
@@ -244,21 +244,18 @@ Homarr supports multiple authentication options, from internal userbase (credent
- Create a group (e.g., `homarr-admins`) and assign users who require admin access. - Create a group (e.g., `homarr-admins`) and assign users who require admin access.
**2. Example .env file:** **2. Example .env file:**
Generate `SECRET_ENCRYPTION_KEY` with `openssl rand -hex 32`; do not copy an example key between installs.
```env ```env
OIDC_CLIENT_ID=<REPLACE_WITH_OIDC_CLIENT_ID> OIDC_CLIENT_ID=identificationid #OIDC client ID
OIDC_CLIENT_SECRET=<REPLACE_WITH_OIDC_CLIENT_SECRET> OIDC_CLIENT_SECRET=secretsecretsecret #OIDC client secret
OIDC_SLUG=homarr #Application slug in Authentik OIDC_SLUG=homarr #Application slug in Authentik
AUTH_DOMAIN=auth.example.com #Authentik FQDN AUTH_DOMAIN=auth.example.com #Authentik FQDN
ADMIN_GROUP=homarr-admins #Authentik group for Homarr admins ADMIN_GROUP=homarr-admins #Authentik group for Homarr admins
HOMARR_FQDN=homarr.example.com #Homarr FQDN HOMARR_FQDN=homarr.example.com #Homarr FQDN
SECRET_ENCRYPTION_KEY=<REPLACE_WITH_64_HEX_CHARACTERS> SECRET_ENCRYPTION_KEY=encryptencryptencrypt #Homarr encryption key
``` ```
**3. Example Docker Compose configuration:** **3. Example Docker Compose configuration:**
```yaml {14-24} ```yaml {14-26}
services: services:
homarr: homarr:
image: ghcr.io/homarr-labs/homarr:latest image: ghcr.io/homarr-labs/homarr:latest
@@ -272,6 +269,8 @@ Homarr supports multiple authentication options, from internal userbase (credent
environment: environment:
- TZ=America/Los_Angeles - TZ=America/Los_Angeles
- SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY} - SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY}
- BASE_URL=https://${HOMARR_FQDN}
- NEXTAUTH_URL=https://${HOMARR_FQDN}
- AUTH_PROVIDERS=oidc #(optional: include ',credentials' to keep local accounts as fallback) - AUTH_PROVIDERS=oidc #(optional: include ',credentials' to keep local accounts as fallback)
- AUTH_OIDC_CLIENT_ID=${OIDC_CLIENT_ID} - AUTH_OIDC_CLIENT_ID=${OIDC_CLIENT_ID}
- AUTH_OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET} - AUTH_OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
@@ -323,18 +322,15 @@ Homarr supports multiple authentication options, from internal userbase (credent
- Record the `Client-ID` and `Client Key` (secret). - Record the `Client-ID` and `Client Key` (secret).
**2. Example .env file (Homarr):** **2. Example .env file (Homarr):**
Generate `SECRET_ENCRYPTION_KEY` with `openssl rand -hex 32`; do not copy an example key between installs.
```env ```env
OIDC_CLIENT_ID=<REPLACE_WITH_GOOGLE_CLIENT_ID> OIDC_CLIENT_ID=identificationid #Google Client-ID
OIDC_CLIENT_SECRET=<REPLACE_WITH_GOOGLE_CLIENT_SECRET> OIDC_CLIENT_SECRET=secretsecretsecret #Google Client Key (secret)
HOMARR_FQDN=homarr.example.com #Homarr FQDN HOMARR_FQDN=homarr.example.com #Homarr FQDN
SECRET_ENCRYPTION_KEY=<REPLACE_WITH_64_HEX_CHARACTERS> SECRET_ENCRYPTION_KEY=encryptencryptencrypt #Homarr encryption key
``` ```
**3. Example Docker Compose configuration (Homarr):** **3. Example Docker Compose configuration (Homarr):**
```yaml {14-20} ```yaml {16-22}
services: services:
homarr: homarr:
image: ghcr.io/homarr-labs/homarr:latest image: ghcr.io/homarr-labs/homarr:latest
@@ -348,6 +344,8 @@ Homarr supports multiple authentication options, from internal userbase (credent
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY} - SECRET_ENCRYPTION_KEY=${SECRET_ENCRYPTION_KEY}
- BASE_URL=https://${HOMARR_FQDN}
- NEXTAUTH_URL=https://${HOMARR_FQDN}
- AUTH_PROVIDERS=oidc #(optional: include ',credentials' to keep local accounts as fallback) - AUTH_PROVIDERS=oidc #(optional: include ',credentials' to keep local accounts as fallback)
- AUTH_OIDC_CLIENT_ID=${OIDC_CLIENT_ID} - AUTH_OIDC_CLIENT_ID=${OIDC_CLIENT_ID}
- AUTH_OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET} - AUTH_OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
@@ -38,14 +38,13 @@ This method is only useful, when you want to develop on Homarr or extend it with
Install all dependencies using <code>pnpm install</code> Install all dependencies using <code>pnpm install</code>
</li> </li>
<li> <li>
Copy the `.env.example` file to `.env`. Its `DB_URL='./db.sqlite'` value is ready for local use and stores the Copy the `.env.example` file to `.env` and replace `DB_URL` with the full path you want to use for the sqlite
database in the cloned repository. Replace it with an absolute writable path such as database file. For example `DB_URL='/home/username/homarr/db.sqlite`
`DB_URL='/home/username/homarr/db.sqlite'` if you want to store the database elsewhere.
</li> </li>
<li> <li>
Run `openssl rand -hex 32` twice and set `AUTH_SECRET` and `SECRET_ENCRYPTION_KEY` to the two different generated Replace `AUTH_SECRET` and `SECRET_ENCRYPTION_KEY` both with a secure random string by using `openssl rand -hex 32`
values. Keep both values secret and preserve `SECRET_ENCRYPTION_KEY` with your backups.
</li> </li>
<li>Replace `CRON_JOB_API_KEY` with a secure random string by using `openssl rand -base64 32`</li>
<li>Run `pnpm run db:migration:sqlite:run` and wait that it completes</li> <li>Run `pnpm run db:migration:sqlite:run` and wait that it completes</li>
<li>Build the source using `pnpm build`</li> <li>Build the source using `pnpm build`</li>
<li> <li>
+5 -4
View File
@@ -481,13 +481,14 @@ export function calculateCpuUsage(stats: ContainerStats): number {
return 0; return 0;
} }
const numberOfCpus = stats.cpu_stats.online_cpus; const cpuDelta = stats.cpu_stats.cpu_usage.total_usage - (stats.precpu_stats?.cpu_usage?.total_usage ?? 0);
const usage = stats.cpu_stats.system_cpu_usage; const systemDelta = (stats.cpu_stats.system_cpu_usage ?? 0) - (stats.precpu_stats?.system_cpu_usage ?? 0);
if (!usage || usage === 0) {
if (systemDelta <= 0 || cpuDelta < 0) {
return 0; return 0;
} }
return (stats.cpu_stats.cpu_usage.total_usage / usage) * numberOfCpus * 100; return (cpuDelta / systemDelta) * 100;
} }
export function calculateMemoryUsage(stats: ContainerStats): number { export function calculateMemoryUsage(stats: ContainerStats): number {
@@ -72,16 +72,16 @@ describe("calculateCpuUsage", () => {
const stats = createStats({ const stats = createStats({
cpu_stats: { online_cpus: 4, cpu_usage: { total_usage: 2000 }, system_cpu_usage: 10000 }, cpu_stats: { online_cpus: 4, cpu_usage: { total_usage: 2000 }, system_cpu_usage: 10000 },
}); });
// (2000 / 10000) * 4 * 100 = 80 // (2000 / 10000) * 100 = 20
expect(calculateCpuUsage(stats)).toBe(80); expect(calculateCpuUsage(stats)).toBe(20);
}); });
test("should handle fractional CPU usage", () => { test("should handle fractional CPU usage", () => {
const stats = createStats({ const stats = createStats({
cpu_stats: { online_cpus: 2, cpu_usage: { total_usage: 500 }, system_cpu_usage: 100000 }, cpu_stats: { online_cpus: 2, cpu_usage: { total_usage: 500 }, system_cpu_usage: 100000 },
}); });
// (500 / 100000) * 2 * 100 = 1 // (500 / 100000) * 100 = 0.5
expect(calculateCpuUsage(stats)).toBe(1); expect(calculateCpuUsage(stats)).toBe(0.5);
}); });
}); });