Skip to content

Instantly share code, notes, and snippets.

@MrModest
Last active January 17, 2025 16:12
Show Gist options
  • Save MrModest/cca4d3f414cec06c88a374a1c1feb9e6 to your computer and use it in GitHub Desktop.
Save MrModest/cca4d3f414cec06c88a374a1c1feb9e6 to your computer and use it in GitHub Desktop.
# src/composes/immich/compose.d8s.yml
kind: compose
name: {{ .Name }}
serviceCommons: # shortcut to describe the same property value for each service
user: {{ .User }}
restart: {{ .RestartMode }}
networks:
- {{ .Networks.ReverseProxy }}
env: {{ .Name }}_envs # id of `env_vars` kind file
services:
- name: app # container_name and hostname is '<compose_name>_<service_name>'
image: ghcr.io/immich-app/immich-server
version: {{ .Versions.App }}
volumes:
- path: /usr/src/app
volumeId: {{ .Name }}_upload
- path: /usr/src/app/external
volumeId: {{ .Name }}_external
limits:
cpu: 3
- name: machine-learning
image: ghcr.io/immich-app/immich-machine-learning
version: {{ .Versions.ML }}
limits:
cpu: 2
- name: redis
image: docker.io/redis
version: {{ .Versions.Redis }}
volumes:
- path: /data
volumeId: {{ .Name }}_redis
healthcheck:
test: redis-cli ping || exit 1
interval: 10s
timeout: 5s
retries: 5
- name: postgres
image: docker.io/tensorchord/pgvecto-rs
version: {{ .Versions.Postgres }}
environment:
POSTGRES_PASSWORD: {{ .Vault.Immich.DbPassword }}
POSTGRES_USER: {{ .Vault.Immich.DbUser }}
POSTGRES_DB: {{ .Vault.Immich.DbName }}
volumes:
- path: /var/lib/postgresql/data
volumeId: {{ .Name }}_postgres
healthcheck:
test: {{ .HealthChecks.Postgres.TestCmd }}
interval: 5m
start_interval: 30s
start_period: 5m
kind: env_vars
id: {{ .Name }}_envs
values:
PGUSER: immich
PGDATABASE: immich
PGPORT: 5432
PGPASSWORD: {{ .Vault.Immich.DbPassword }}
- root.d8s.yml
- apps/
- immich/
- compose.d8s.yml
- volumes.d8s.yml
- values.d8s.yml # path sensitive
- envs.d8s.yml
- values/
- global-values.d8s.yml # can be splitted into different files for convinience, but still seen globally
- volumes/
- shared/
shared-volume-1.d8s.yml
shared-volume-2.d8s.yml
- immich/
- volumes.d8s.yml
- vaults/
- vault.d8s.yml # same as `kind: values` but can be encrypted and meant to be in `.gitignore`
# src/values/global.d8s.yml
kind: values
scope: private
values:
PoolsRoot: /mnt/pools
---
kind: values
scope: global # can be referenced anywhere in the project
values:
Global:
FileSystem:
Volumes:
FastRoot: {{ .PoolsRoot }}/fast-tank
SlowRoot: {{ .PoolsRoot }}/slow-tank
# src/root.d8s.yml
kind: root
serverName: homeserver
dockerSocket: /var/run/docker.sock
# src/composes/immich/values.d8s.yml
kind: values
scope: private # can be referenced only in this file
values:
AppVersion: v1.113.0
---
kind: values
scope: internal # can be referenced only in the same folder's files
values:
Name: immich
Versions:
App: {{ .AppVersion }}
ML: {{ .AppVersion }}
Redis: 6.2-alpine@sha256:e3b17ba9479deec4b7d1eeec1548a253acc5374d68d3b27937fcfe4df8d18c7e
Postgres: pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0
HealthCheck:
Postgres:
TestCmd: pg_isready --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT COALESCE(SUM(checksum_failures), 0) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
# src/composes/immich/volumes.d8s.yml
kind: volume
id: {{ .Name }}_upload
name: upload # folder name; it's equal to 'id' by default, but can be overridden
parentDir: {{ .Global.FileSystem.Volumes.SlowRoot }}/{{ .Name }}
permissions:
owner: {{ .User }}:{{ .Group }}
mode: '0754' # it's the default value
recoursive: true # default value
labels:
- app_using: {{ .Name }} # label with the same key is allowed; for example, several apps can use a common volume
---
kind: volumeGroup
parentDir: {{ .Global.FileSystem.Volumes.FastRoot }}/{{ .Name }}
permissions:
owner: {{ .User }}:{{ .Group }}
volumes:
- id: {{ .Name }}_external
name: external
- id: {{ .Name }}_redis
name: redis
- id: {{ .Name }}_postgres
name: postgres
labels:
- app_using: {{ .Name }}
@MrModest
Copy link
Author

version: "3.9"

defaults:
  restart: unless-stopped
  env_file:
    - .env
  container_name: "${{service}}"

volumes:
  grafana_data:
    host_path: '${APP_DATA}/grafana/data'
    permissions: 'rw'
    owner: ${APP_USER}
  loki_data:
    docker_volume: true
    driver: local 
    driver_opts:
      type: btrfs
      device: /dev/sdb1
    external: true
    name: my-loki-data  # Explicitly set the volume name
    labels:
      my-label: "some value"

services:
  # ... services ...

@MrModest
Copy link
Author

MrModest commented Nov 16, 2024

It might makes sense to change the approach to have templates aka functions that allows to have a quickly configured setup with a liited variables set and all other as default values.

Here's an example of pretty long and verbose docker-compose.yml: https://gitlab.com/baserow/baserow/-/blob/master/docker-compose.yml

So, it's would be nice if soneone could declare a template and share it with others, so they don't have to write their own compose template

So, something like TrueCharts but for compose files.

What's might be needed to run a compose-based project:

  • compose.yml file
  • .env file
  • volumes
    • the template can provide paths that require volume mounting as an input param so a user can decide where to mount them in thirs host machine
  • user/group
  • configs/other files for the app to be able to run properly

  • Template can be imported as a path to the git repository or local folder.
  • Template can have its own values.yml (but called inputs instead), but user can override any values in it by providing their own values file
  • Template can provide a required flag instead of a default value to tell the user "You have to set this variable yourself, there's no default value"

Example of template usage:

kind: template
git: https://github.com/MrModest/immich-d8s-template
version: ed94fe4775d0b04c4ba3dd9b7ff683ee35ec8730 # optional, can be commit sha or branch name

or if local

# src/composes/immich/app.d8s.yml
kind: template
path: ../../templates/immich/template.d8s.yml
# or absolute (from root, configured in the conf.yml)
# path: @/templates/immich/template.d8s.yml

@MrModest
Copy link
Author

@MrModest
Copy link
Author

MrModest commented Jan 17, 2025

data class DockerImage(
    val repository: String,
    val tag: String
)

data class ContainerUser(
    val uid: Int,
    val gid: Int 
)

enum class RestartPolicy {
    No,
    Always,
    OnFailure,
    UnlessStopped
}

data class Volume(
    val hostPath: String,
    val containerPath: String
)

data class Port(
    val host: Int,
    val container: Int
)

data class DeployResources(
    val cpus: Double?,
    val memory: String
)

data class Deploy(
    val limits: DeployResources,
    val reservarions: DeployResources,
    val replicas: Int?
)

data class Build(
    val context: String?,
    val dockerfile: String,
    val args: Map<String, String>
)

data class HealthCheck(
    val test: String,
    val interval: String,
    val startInterval: String,
    val StartPeriod: String,
)

data class ComposeService(
    val name: String,
    val build: Build?,
    val image: DockerImage?,
    val containerName: String?,
    val hostname: String?,
    val user: ContainerUser,
    val restart: RestartPolicy,
    val volumes: List<Volume>,
    val envs: Map<String, String>,
    val envFiles: List<String>,
    val dependsOn: List<String>,
    val labels: Map<String, String>,
    val labelFiles: List<String>,
    val deploy: Deploy?,
    val port: List<Port>,
    val healthcheck: HealthCheck?,
    val command: String,
    val networks: List<String>
)
service(
    name = "immich-server",
    image = Image("ghcr.io/immich-app/immich-server", "${config.apps.versions.immich.server}")
    volumes = listOf(
        Volume("${config.apps.volumes.fast.basePath}/immich/upload", "/usr/src/app/upload")
        Volume("${config.apps.volumes.slow.basePath}/immich/external", "/usr/src/app/upload")
        Volume("/etc/localtime", "/etc/localtime", readOnly = true)
    ),
    envFiles = listOf(
        ".env"
    ),
    dependsOn = listOf("redis", "database"),
    deploy = Deploy(
        resources = DeployResources(
            limits = DeployResourcesConfig(
                cpus = "3"
            )
        )
    )
)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment