../../_images/kup6s-icon-deployment.svg

Penpot design platform

Self-hosted Penpot (MPL-2.0, Clojure/ClojureScript) design and prototyping platform deployed at penpot.kup.tirol. It replaces paid Figma seats for team and customer design work.

Overview

Penpot runs as five deployments from the upstream Helm chart, wrapped in a K3S HelmChart resource.

Component

Port

Replicas

Purpose

Frontend

8080

2

nginx serving the SPA and proxying every other component

Backend

6060

1

API, authentication, file storage

Exporter

6061

1

Headless Chromium for PNG, SVG, and PDF export

MCP

4401, 4402

1

Model Context Protocol server for agent access to designs

Admin console

3000

1

Instance and user administration

Only the frontend is exposed through the ingress. It proxies the other components internally over PENPOT_BACKEND_URI, PENPOT_EXPORTER_URI, PENPOT_MCP_URI, and PENPOT_ADMIN_CONSOLE_URI.

Access

Open the app at https://penpot.kup.tirol.

Registration is open only to email addresses in registrationDomainWhitelist, currently kleinundpartner.at. Invitations do not bypass that whitelist, so onboarding a customer means adding their domain first. See How to onboard team members and customers.

The admin console lives at /admin-console and redirects to the normal Penpot login.

Warning

Outgoing mail does not work while Penpot runs on a dedicated EX worker. Mailjet resets connections from the egress IPs of kup6s-ex-hel-1 and kup6s-ex-hel-2 on every port, so verification and invitation mails never leave the cluster. Mailjet support ticket 4282793 tracks the unblock request. Until it is resolved, activate a new account manually — see Troubleshoot Penpot.

Warning

The MCP endpoints under /mcp answer unauthenticated callers. An anonymous client can complete the MCP handshake and list tools, including execute_code. Penpot states that a user must first connect a design project with the Penpot MCP plugin before those tools act on anything, but the pairing model has not been audited here. Treat the MCP surface as untrusted until it is restricted or verified.

Architecture

Concern

Implementation

Source

dp-infra/penpot/ (CDK8S, TypeScript), ArgoCD-managed

Chart

penpot/penpot 1.10.0 from https://helm.penpot.app, app version 2.18.0

Database

CloudNativePG penpot-postgres (PG 16, 2 instances, longhorn), direct connection to penpot-postgres-rw

Database backups

barman-cloud plugin to backups-penpot-kup6s (fsn1, cold), daily at 02:45 plus WAL

Design assets

Hetzner S3 assets-penpot-kup6s (hel1, hot, near the EX workers)

Cache and sessions

Valkey StatefulSet penpot-valkey (1 replica, 1Gi longhorn)

Secrets

External Secrets Operator, bridged from the application-secrets namespace

Mail

Mailjet relay, shared with the other deployments

Ingress

Traefik with letsencrypt-cluster-issuer, host penpot.kup.tirol

File data stays in PostgreSQL (fileDataBackend: legacy-db). Only assets go to S3, because the object storage backend for file data has an open upstream bug with shared library absorption.

There is no PgBouncer pooler. The backend caps its own JDBC pool with PENPOT_DATABASE_MAX_POOL_SIZE, because Penpot otherwise holds 60 connections open per replica.