version: '3.8' # Penpot - Open Source Design Platform # Purpose: Self-hosted Figma alternative for design and prototyping # Port: 9001 (web UI) # GPU: No # External: penpot.schweitz.net # Storage: SSD (assets), PostgreSQL shared, Redis shared (DB 10) services: # ============================================ # Penpot Frontend - Web UI (nginx) # Port: 9001 (exposed) # ============================================ penpot-frontend: image: penpotapp/frontend:latest container_name: penpot-frontend restart: unless-stopped ports: - "9001:8080" volumes: - /home/jpmschweitzer/docker-data/penpot/assets:/opt/data/assets environment: # Feature flags PENPOT_FLAGS: enable-login-with-oidc disable-login-with-password disable-registration enable-oidc-registration disable-email-verification # Backend connection (internal DNS) PENPOT_BACKEND_URI: http://penpot-backend:6060 PENPOT_EXPORTER_URI: http://penpot-exporter:6061 # Timezone TZ: Europe/Amsterdam networks: - docker-dataplane depends_on: - penpot-backend - penpot-exporter labels: - "com.centurylinklabs.watchtower.enable=true" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080"] interval: 30s timeout: 10s retries: 3 start_period: 30s # ============================================ # Penpot Backend - API Server (Clojure) # Internal only - connects to postgres-shared + redis-shared # ============================================ penpot-backend: image: penpotapp/backend:latest container_name: penpot-backend restart: unless-stopped volumes: - /home/jpmschweitzer/docker-data/penpot/assets:/opt/data/assets environment: # Public URI (for callbacks and links) PENPOT_PUBLIC_URI: https://penpot.schweitz.net # Feature flags (same as frontend) PENPOT_FLAGS: enable-login-with-oidc disable-login-with-password disable-registration enable-oidc-registration disable-email-verification # PostgreSQL (shared) PENPOT_DATABASE_URI: postgresql://postgres-shared:5432/penpot PENPOT_DATABASE_USERNAME: penpot_user PENPOT_DATABASE_PASSWORD: YLmawuhcm0TLEMztkg/2Rzw+oAV6KPgHa3dZEYTkWr8= # Redis (shared, DB 10) PENPOT_REDIS_URI: redis://redis-shared:6379/10 # Assets storage (updated for Penpot 2.11+) PENPOT_OBJECTS_STORAGE_BACKEND: fs PENPOT_OBJECTS_STORAGE_FS_DIRECTORY: /opt/data/assets # Secret key (512-bit base64) PENPOT_SECRET_KEY: +g+fSbAzBWVA2gE8ZnJUcpgq9L1BCqtuB+h9w73iF9qcgxhjMF1QU23XUjQ5ZdxpPmwoyuuQEm/frtKUV2M6pA== # Telemetry (disabled) PENPOT_TELEMETRY_ENABLED: false # ============================================ # Authentik OIDC Configuration # ============================================ PENPOT_OIDC_CLIENT_ID: ${PENPOT_OIDC_CLIENT_ID} PENPOT_OIDC_CLIENT_SECRET: ${PENPOT_OIDC_CLIENT_SECRET} PENPOT_OIDC_BASE_URI: https://auth.schweitz.net/application/o/penpot/ PENPOT_OIDC_AUTH_URI: https://auth.schweitz.net/application/o/authorize/ PENPOT_OIDC_TOKEN_URI: https://auth.schweitz.net/application/o/token/ PENPOT_OIDC_USER_URI: https://auth.schweitz.net/application/o/userinfo/ PENPOT_OIDC_SCOPES: openid profile email PENPOT_OIDC_NAME_ATTR: name PENPOT_OIDC_EMAIL_ATTR: email # SMTP Configuration (optional - for notifications) # PENPOT_SMTP_DEFAULT_FROM: penpot@schweitz.net # PENPOT_SMTP_DEFAULT_REPLY_TO: penpot@schweitz.net # PENPOT_SMTP_HOST: smtp.example.com # PENPOT_SMTP_PORT: 587 # PENPOT_SMTP_USERNAME: username # PENPOT_SMTP_PASSWORD: password # PENPOT_SMTP_TLS: true # Timezone TZ: Europe/Amsterdam networks: - docker-dataplane labels: - "com.centurylinklabs.watchtower.enable=true" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:6060/readyz"] interval: 30s timeout: 10s retries: 3 start_period: 60s deploy: resources: limits: memory: 1G reservations: memory: 256M # ============================================ # Penpot Exporter - PDF/SVG Export Worker # Internal only # ============================================ penpot-exporter: image: penpotapp/exporter:latest container_name: penpot-exporter restart: unless-stopped environment: # Public URI (for rendering) PENPOT_PUBLIC_URI: https://penpot.schweitz.net # Redis (shared, DB 10) PENPOT_REDIS_URI: redis://redis-shared:6379/10 # Secret key (must match backend) PENPOT_SECRET_KEY: +g+fSbAzBWVA2gE8ZnJUcpgq9L1BCqtuB+h9w73iF9qcgxhjMF1QU23XUjQ5ZdxpPmwoyuuQEm/frtKUV2M6pA== # Timezone TZ: Europe/Amsterdam networks: - docker-dataplane labels: - "com.centurylinklabs.watchtower.enable=true" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:6061/readyz"] interval: 30s timeout: 10s retries: 3 start_period: 30s deploy: resources: limits: memory: 1G reservations: memory: 256M networks: docker-dataplane: external: true name: docker-dataplane # ============================================================================= # DEPLOYMENT INSTRUCTIONS # ============================================================================= # # Phase 1: Database Setup (DONE) # Database 'penpot' and user 'penpot_user' created in postgres-shared # # Phase 2: Authentik OIDC Configuration # 1. Go to https://auth.schweitz.net/if/admin/#/core/providers # 2. Create new OAuth2/OpenID Provider: # - Name: Penpot # - Authorization flow: default-provider-authorization-implicit-consent # - Client type: Confidential # - Client ID: (auto-generated, copy to Portainer env vars) # - Client Secret: (auto-generated, copy to Portainer env vars) # - Redirect URIs/Origins: https://penpot.schweitz.net/api/auth/oauth/oidc/callback # - Scopes: openid profile email # - Signing Key: authentik Self-signed Certificate # # 3. Create Application: # - Name: Penpot # - Slug: penpot # - Provider: Penpot (created above) # - Launch URL: https://penpot.schweitz.net # # 4. Add environment variables in Portainer: # PENPOT_OIDC_CLIENT_ID= # PENPOT_OIDC_CLIENT_SECRET= # # Phase 3: Deploy Stack # Deploy via Portainer with the OIDC environment variables # # Phase 4: NPM Proxy Configuration # 1. Create new proxy host in NPM (http://192.168.86.149:81) # 2. Domain: penpot.schweitz.net # 3. Forward Host: 192.168.86.149 # 4. Forward Port: 9001 # 5. Enable: Block Common Exploits, Websockets Support # 6. SSL: Request new Let's Encrypt certificate, Force SSL, HTTP/2 # # Phase 5: Verify Deployment # 1. Access https://penpot.schweitz.net # 2. Click "Login with OIDC" - should redirect to auth.schweitz.net # 3. Login with Authentik credentials # 4. Verify redirect back to Penpot with authenticated session # 5. Create a test project to verify functionality # # ============================================================================= # CREDENTIALS (stored in Portainer env vars) # ============================================================================= # PostgreSQL: # Host: postgres-shared:5432 # Database: penpot # User: penpot_user # Password: YLmawuhcm0TLEMztkg/2Rzw+oAV6KPgHa3dZEYTkWr8= # # Redis: # Host: redis-shared:6379 # Database: 10 # # Secret Key: +g+fSbAzBWVA2gE8ZnJUcpgq9L1BCqtuB+h9w73iF9qcgxhjMF1QU23XUjQ5ZdxpPmwoyuuQEm/frtKUV2M6pA==