{ config, lib, pkgs, ... }: let cfg = config.homelab.apps.penpot; networkName = "penpot"; UID = config.users.users.apps.uid; GID = config.users.groups.apps.gid; version = "2.5.4"; srvPath = "/srv/penpot"; assetsPath = "/opt/data/assets"; PENPOT_FLAGS = "enable-smtp enable-prepl-server disable-secure-session-cookies disable-onboarding disable-registration"; # Max body size (30MiB); Used for plain requests, should never be # greater than multi-part size PENPOT_HTTP_SERVER_MAX_BODY_SIZE = "31457280"; # Max multipart body size (350MiB) PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE = "367001600"; dbName = "penpot"; dbUser = "penpot"; dbPass = "penpot"; docker = config.virtualisation.oci-containers.containers; in { options.homelab.apps.penpot.enable = lib.mkEnableOption "Penpot using Docker"; config = lib.mkIf cfg.enable { homelab = { users.apps.enable = true; virtualisation.containers.enable = true; }; fileSystems."${srvPath}" = { device = "192.168.0.11:/mnt/SMALL/CONFIG/PENPOT"; fsType = "nfs"; options = [ "rw" "nfsvers=4.2" "sync" "hard" "timeo=600" "retrans=2" "_netdev" "nosuid" "tcp" ]; }; # Make sure the Docker network exists. systemd.services."docker-${networkName}-create-network" = lib.mkIf cfg.enable { description = "Create Docker network for ${networkName}"; requiredBy = [ "docker-penpot-frontend.service" "docker-penpot-backend.service" "docker-penpot-exporter.service" "docker-penpot-postgres.service" "docker-penpot-redis.service" ]; serviceConfig = { Type = "oneshot"; RemainAfterExit = true; }; script = '' if ! ${pkgs.docker}/bin/docker network ls | grep -q ${networkName}; then ${pkgs.docker}/bin/docker network create ${networkName} fi ''; }; virtualisation.oci-containers.containers = let frontendPort = 8080; redisUri = "redis://${docker.penpot-redis.hostname}/0"; in { penpot-frontend = { hostname = "penpot-frontend"; image = "penpotapp/frontend:${version}"; # user = "${toString UID}:${toString GID}"; user = "0:${toString GID}"; ports = [ # "9001:${toString frontendPort}/tcp" ]; extraOptions = [ "--network=${networkName}" ]; environment = { inherit PENPOT_FLAGS; inherit PENPOT_HTTP_SERVER_MAX_BODY_SIZE; inherit PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE; }; volumes = [ "${srvPath}:${assetsPath}" ]; dependsOn = [ docker.penpot-backend.hostname docker.penpot-exporter.hostname ]; labels = { "traefik.enable" = "true"; "traefik.http.routers.penpot.rule" = "Host(`penpot.depeuter.dev`)"; "traefik.http.services.penpot.loadbalancer.server.port" = toString frontendPort; "traefik.tls.options.default.minVersion" = "VersionTLS13"; }; autoStart = true; }; penpot-backend = { hostname = "penpot-backend"; image = "penpotapp/backend:latest"; # user = "${toString UID}:${toString GID}"; user = "0:${toString GID}"; extraOptions = [ "--network=${networkName}" ]; environmentFiles = [ /home/admin/.penpot.secret ]; environment = { inherit PENPOT_FLAGS; inherit PENPOT_HTTP_SERVER_MAX_BODY_SIZE; inherit PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE; PENPOT_PUBLIC_URI = "https://penpot.depeuter.dev"; ## Database connection parameters. Don't touch them unless you are using custom ## postgresql connection parameters. PENPOT_DATABASE_URI = "postgresql://${docker.penpot-postgres.hostname}/${dbName}"; PENPOT_DATABASE_USERNAME = dbUser; PENPOT_DATABASE_PASSWORD = dbPass; ## Redis is used for the websockets notifications. Don't touch unless the redis ## container has different parameters or different name. PENPOT_REDIS_URI = redisUri; ## Default configuration for assets storage: using filesystem based with all files ## stored in a docker volume. PENPOT_ASSETS_STORAGE_BACKEND = "assets-fs"; PENPOT_STORAGE_ASSETS_FS_DIRECTORY = assetsPath; ## Telemetry. When enabled, a periodical process will send anonymous data about this ## instance. Telemetry data will enable us to learn how the application is used, ## based on real scenarios. If you want to help us, please leave it enabled. You can ## audit what data we send with the code available on github. PENPOT_TELEMETRY_ENABLED = "false"; PENPOT_TELEMETRY_REFERER = "compose"; # PENPOT_SMTP_HOST = "smtp.gmail.com"; # PENPOT_SMTP_PORT = "465"; # PENPOT_SMTP_USERNAME: "kmtl.hugo@gmail.com"; # PENPOT_SMTP_PASSWORD: # PENPOT_SMTP_TLS: true }; volumes = [ "${srvPath}:${assetsPath}" ]; dependsOn = [ docker.penpot-postgres.hostname docker.penpot-redis.hostname ]; autoStart = true; }; penpot-exporter = { hostname = "penpot-exporter"; image = "penpotapp/exporter:latest"; extraOptions = [ "--network=${networkName}" ]; environment = { # Don't touch it; this uses an internal docker network to # communicate with the frontend. PENPOT_PUBLIC_URI = "http://${docker.penpot-frontend.hostname}:${toString frontendPort}"; ## Redis is used for the websockets notifications. PENPOT_REDIS_URI = redisUri; }; dependsOn = [ docker.penpot-redis.hostname ]; autoStart = true; }; penpot-postgres = { hostname = "penpot-postgres"; image = "postgres:15"; extraOptions = [ "--network=${networkName}" "--health-cmd='pg_isready -U ${dbUser}'" "--health-interval=2s" "--health-retries=5" "--health-timeout=10s" "--health-start-period=2s" ]; environment = { POSTGRES_INITDB_ARGS = "--data-checksums"; POSTGRES_DB = dbName; POSTGRES_USER = dbUser; POSTGRES_PASSWORD = dbPass; }; volumes = [ "penpot_postgres_v15:/var/lib/postgresql/data" ]; autoStart = true; }; penpot-redis = { hostname = "penpot-redis"; image = "redis:7.2"; extraOptions = [ "--network=${networkName}" "--health-cmd='redis-cli ping | grep PONG'" "--health-interval=1s" "--health-retries=5" "--health-timeout=3s" "--health-start-period=3s" ]; autoStart = true; }; }; }; }