local optional(object, field) = std.get(object, field, {}); local dockerSocket = '/var/run/docker.sock:/var/run/docker.sock'; local tz = 'America/New_York'; local mediaEnv = { PUID: 1000, PGID: 1000, TZ: tz, }; local formatHelper(fmt) = function(name, option) fmt % [name, option]; local toList(fmt) = function(object) std.objectValues(std.mapWithKey(formatHelper(fmt), object)); local toLabels = toList('%s=%s'); local toVolumes = toList('%s:%s'); local toCommand = toList('--%s=%s'); local traefikLabels(name, host, port, extras) = toLabels({ 'traefik.enable': 'true', ['traefik.http.routers.%s.rule' % name]: 'Host(`%s.mat`)' % host, ['traefik.http.routers.%s.entrypoints' % name]: 'web', ['traefik.http.services.%s.loadbalancer.server.port' % name]: port, ['traefik.http.routers.%s.service' % name]: '%s' % name, 'traefik.docker.network': 'traefik', } + extras); local mkService(name, svc) = svc { container_name: name, networks: ['traefik'], volumes: toVolumes(optional(svc, 'volumes')) + toVolumes(optional(svc, 'mounts')) + if std.get(svc, 'docker', false) then [dockerSocket] else [], labels: traefikLabels(name, std.get(svc, 'host', name), svc.webPort, optional(svc, 'traefik')), restart: 'always', }; local extractVolumes(cfg) = { [name]: { external: true } for name in std.flattenArrays([ std.objectFields(optional(svc, 'volumes')) for svc in std.objectValues(cfg) ]) }; local Compose(cfg) = { services: std.mapWithKey(mkService, cfg), volumes: extractVolumes(cfg), networks: { traefik: { external: true } }, }; local Port(port, src=port, kind='tcp') = local mapped = '%d:%d' % [port, src]; '%s/%s' % [mapped, kind]; local MediaMounts(mounts) = { ['/media/mat/%s' % path]: mounts[path] for path in std.objectFields(mounts) }; local MediaService(name, tag='latest', env={}, mounts={}, webPort, ports=[]) = { image: 'lscr.io/linuxserver/%s:%s' % [name, tag], environment: mediaEnv + env, volumes: { ['%s_config' % name]: '/config' }, mounts:: MediaMounts(mounts), webPort:: webPort, ports: ports, }; function(secrets={}) Compose({ traefik: { image: 'traefik:latest', command: toCommand({ 'log.level': 'ERROR', 'api.insecure': 'true', 'providers.docker': 'true', 'providers.docker.exposedbydefault': 'false', 'entrypoints.web.address': ':80', // 'entrypoints.websecure.address': ':443', }), docker:: true, webPort:: 80, ports: [Port(80), /* Port(443), */ Port(8080)], traefik:: { // 'traefik.http.routers.http-catchall.rule': 'hostregexp(`{host:.+}`)' // 'traefik.http.routers.http-catchall.entrypoints': 'web' // 'traefik.http.routers.http-catchall.middlewares': 'redirect-to-https' // 'traefik.http.middlewares.redirect-to-https.redirectscheme.scheme': 'https' }, }, portainer: { image: 'portainer/portainer-ce:latest', docker:: true, volumes: { portainer_data: '/data' }, webPort:: 9000, ports: [Port(9443)], }, deluge: MediaService( name='deluge', env={ DELUGE_LOGLEVEL: 'error' }, mounts={ torrents: '/downloads' }, webPort=8112, ports=[Port(54979), Port(54979, kind='udp')], ), prowlarr: MediaService( name='prowlarr', tag='develop', webPort=9696, mounts={ torrents: '/downloads', 'passport-5tb': '/passport-5tb', 'passport-1tb': '/passport-1tb', }, ), bazarr: MediaService( name='bazarr', webPort=6767, mounts={ 'passport-5tb': '/passport-5tb', 'passport-1tb': '/passport-1tb', }, ), radarr: MediaService( name='radarr', webPort=7878, mounts={ 'passport-5tb/movies': '/passport-5tb', 'passport-1tb/movies': '/passport-1tb', torrents: '/downloads', }, ), sonarr: MediaService( name='sonarr', webPort=8989, mounts={ 'passport-5tb/tv': '/passport-5tb', 'passport-1tb/tv': '/passport-1tb', torrents: '/downloads', } ), plex: { image: 'plexinc/pms-docker', environment: { TZ: tz, PLEX_CLAIM: std.get(secrets, 'PLEX_CLAIM'), ADVERTISE_IP: std.get(secrets, 'ADVERTISE_IP'), }, volumes: { plex_config: '/config' }, mounts:: MediaMounts({ 'torrents/plex-transcode': '/transcode', 'passport-5tb': '/passport-5tb', 'passport-1tb': '/passport-1tb', }), devices: ['/dev/dri:/dev/dri'], webPort:: 32400, ports: [ Port(56463, src=32400), Port(3005), Port(8324), Port(32469), Port(1900, kind='udp'), Port(32410, kind='udp'), Port(32412, kind='udp'), Port(32413, kind='udp'), Port(32414, kind='udp'), ], }, archivebox: { image: 'archivebox/archivebox:dev', command: 'server --quick-init 0.0.0.0:8000', environment: { ALLOWED_HOSTS: '*', MEDIA_MAX_SIZE: '750m', RESOLUTION: '1024,768', }, mounts:: MediaMounts({ 'passport-5tb/archivebox': '/data' }), webPort:: 8000, host:: 'archive', }, })