kurogeek 08b5496980 Package Penpot 2.18.3 for Nix with NixOS module; afk devShell
Vendored from debling/nix-penpot (MIT). Fixes on top:
- pnpm fetchers: drop --config.fetch-* flags (UND_ERR_INVALID_ARG on pnpm 11.27)
  and pin real store hashes (frontend x3, exporter, mcp)
- deps-lock.json: add transitive funcool/beicon2 v2.0 so fake-git resolves
  potok2's pin during the shadow-cljs classpath build
- expose frontend pnpm stores via passthru for rehashing
- x86_64-linux only; devShell ships afk, jj, git
2026-10-20 17:45:02 +00:00

penpot-nix

Nix packages and a NixOS module for self-hosting Penpot (pinned to release 2.18.3). Every component is built from upstream source: the Clojure API backend, the static frontend (served by nginx), the headless-Chromium exporter, the MCP server and the Rust/WASM renderer. One module wires the stack together with PostgreSQL and Valkey, either provisioned locally or pointed at infrastructure you already run.

Derived from debling/nix-penpot (MIT, see LICENSE). x86_64-linux only.

Packages

Attribute Output
penpot-backend bin/penpot-backend (JVM wrapper), bin/penpot-manage
penpot-frontend dist/ static bundle (+ plugins/, plugins/mcp/), config.js
penpot-exporter bin/penpot-exporter (node + nixpkgs chromium)
penpot-mcp bin/penpot-mcp
penpot-render-wasm render-wasm.{js,wasm}, shared.js, worker/render.js (frontend)
penpot-render-wasm-export export flavor of the renderer (unused on 2.18.x)

The same packages are exposed as an overlay (overlays.default: pkgs.penpot-backend, pkgs.penpot-frontend, pkgs.penpot-exporter, pkgs.penpot-mcp). The module's package defaults resolve through the overlay, so it is required.

Deploying on NixOS

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    penpot-nix.url = "git+https://<this repo>";
  };

  outputs = { self, nixpkgs, penpot-nix }: {
    nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ./configuration.nix
        { nixpkgs.overlays = [ penpot-nix.overlays.default ]; }
        penpot-nix.nixosModules.default
      ];
    };
  };
}
# configuration.nix
{
  services.penpot = {
    enable = true;
    publicUri = "https://penpot.example.com";
    secretKeyFile = "/run/secrets/penpot-secret-key";
  };
  networking.firewall.allowedTCPPorts = [ 80 443 ];
}

That is a complete deployment: PostgreSQL and Valkey are provisioned locally, the nginx vhost is generated, exporter and MCP run behind it. Secrets are files loaded through systemd credentials; generate the master key once (openssl rand -base64 64 | tr -d '\n') and manage it with agenix/sops-nix.

Commonly tuned options (all documented inline in modules/penpot.nix):

Option Default What it does
publicUri – (required) Public URL of the instance
secretKeyFile – (required) Master secret key file
flags [ "enable-prepl-server" ] Upstream feature flags
admins [ ] Emails granted admin on login
database.createLocally true Local PostgreSQL, or set database.uri for an external one
redis.createLocally true Local Valkey, or set redis.uri
storage.backend "fs" "fs" or "s3" (+ endpoint/bucket/keys)
smtp.* / mailpit.enable off Outgoing email, or a local throwaway SMTP sink
exporter.enable / mcp.enable true Toggle the auxiliary services
nginx.forceHttps false Force HTTPS (configure TLS yourself)

Users and admins

No default credentials. Register through the UI, or on the backend host:

penpot-manage create-profile -n "Jane Doe" -e jane@example.org -p 'secret'

Admin rights come from services.penpot.admins = [ "jane@example.org" ];.

Trying it in a VM

nix build .#nixosConfigurations.penpot-vm.config.system.build.vm
./result/bin/run-penpot-vm-vm

Host port 8080 → nginx, 8025 → mailpit UI. curl http://127.0.0.1:8080/readyz once the backend is up (~1–2 min). Needs ~3 GB RAM.

Development

direnv allow        # or: nix develop — devShell ships the afk harness, jj, git
nix flake check --no-build
nix build .#checks.x86_64-linux.penpot-vm-test   # end-to-end VM test
nix fmt

Development runs under afk (afk is on PATH in the devShell).

Updating Penpot

  1. Bump penpot.url and penpotVersion in flake.nix (upstream's package.json version is stale, so it is pinned by hand).
  2. Regenerate deps-lock.json with clj-nix from a writable copy of the penpot source with the RELEASE pins from packages/frontend.nix (pinDepsEdn) applied:
    nix run github:jlesquembre/clj-nix/<pinned-rev>#deps-lock -- \
      --deps-include backend/deps.edn --deps-include common/deps.edn \
      --deps-include frontend/deps.edn --deps-include exporter/deps.edn \
      --alias-exclude outdated --alias-exclude jvm-repl
    
  3. Rehash the fixed-output derivations (set to lib.fakeHash, build, copy got:): penpot-frontend.{frontendPnpmDeps,pluginsPnpmDeps,mcpPnpmDeps}, penpot-exporter.pnpmDeps, penpot-mcp.pnpmDeps, penpot-backend.builtinTemplates (after nix flake update penpot-files), and cargoDeps / Rust / Skia hashes in packages/render-wasm.nix if render-wasm/Cargo.lock, the Rust version or the Skia URL changed.
  4. Rebase packages/patches/* if frontend/src/app/plugins/library.cljs moved.

License

Nix code: MIT (LICENSE). Penpot: MPL-2.0. The remaining stack (PostgreSQL, Valkey, Chromium, …) ships under its own licenses.

S
Description
No description provided
Readme MIT
119 KiB
0 Stars 1 Watchers 0 Forks
Languages
Nix 92.3%
Python 7.7%