Skip to main content

penpot-local-stack

A disposable Penpot stack for drawing UI mockups, built for AI-driven UI/UX work. Penpot itself is open source; its code is at penpot/penpot.

The stack exists so a design session leaves nothing behind but the drawing. It runs from a Compose file with no persistent state: the database lives in tmpfs and goes away with the containers, the profile is created on every start, and the only thing that survives is templates/ in your directory. Mockups are stored there as unpacked Penpot exports, JSON plus SVG, so git shows readable diffs and an agent can read a mockup directly instead of looking at a picture of it.

Install

Docker with the Compose plugin and Python 3.12 or newer have to be there already, and that python3 needs httpx, questionary, pydantic-settings and PyYAML. The installer checks and tells you what is missing; it never installs anything itself.

Run it in the directory that will hold the mockups:

curl -fsSL https://raw.githubusercontent.com/oberon-systems/penpot-local-stack/main/install/install.sh | bash

It downloads the stack and lays it out:

bin/ libs/ config/   the stack, run by the system python3
.penpot.yaml         settings, read from the directory you run in
Makefile             up, down, import, export, extract, convert
templates/           unpacked Penpot exports, empty at first

Re-running it replaces bin/, libs/ and config/, which is the upgrade, and keeps .penpot.yaml and templates/. --ref unfolds a branch or a tag instead of main. See install/README.md for the details.

Settings

The installer copies .penpot.yaml.example from the repository to .penpot.yaml next to the Makefile, and every command reads that file from the directory it runs in:

templates: templates
host: 127.0.0.1
port: 9001
project: penpot-local
version: 2.17.2
Key Default What it sets
templates templates Where the unpacked exports live
host 127.0.0.1 The address Penpot binds to
port 9001 The port Penpot binds to
project penpot-local The Compose project name
version 2.17.2 The Penpot image tag
email designer@example.com The throwaway profile
password penpot-local Its password

Drop a key to fall back to its default. Every key also answers to an environment variable with a PENPOT_ prefix, and the variable wins over the file:

PENPOT_PORT=9100 make up

Design loop

Start the stack and pick a template or (empty):

make up

up creates a throwaway profile and opens the browser already logged in through the printed /autologin link. If that session is lost, log in as designer@example.com with the password penpot-local.

Draw in the browser at http://localhost:9001, or wherever port points. When you are done, pick the file to export and the template to write it to, then let the stack go down:

make down

down writes the export into templates/<name>/ and runs docker compose down -v, which drops the database and the assets. Pick (skip export) to throw the work away; the wipe is confirmed first whenever the stack still holds a file.

Commands

Target What it does
make up Start the stack, import a template, open the browser
make down Offer the export, confirm the wipe, stop the stack
make import Import a template into the running stack, after a confirm
make export Export a file into templates/, after a confirm
make extract Unpack a .penpot file into templates/
make convert Pack a template into a .penpot file

import and export work against a running stack, so a template can be swapped in or a file saved off without ending the session. Both ask before they touch anything: import names the template it is about to load, export says whether it writes a new template or overwrites one that is already there.

extract and convert are the offline pair and need no stack at all. extract lists the .penpot files next to you, asks which template to write and unpacks the archive under the same rules as export: frame thumbnails are dropped, JSON is reformatted, and an archive holding raster images is refused with the offending files listed. convert goes the other way and packs templates/<name>/ into <name>.penpot, which is what you hand to a Penpot that is not this one. Both confirm before writing, and say whether they are creating something or overwriting it.

Templates

  • One directory per template: manifest.json, files/ and objects/.
  • Templates are read from templates/ in the current directory. Set PENPOT_TEMPLATES to read them from somewhere else.
  • Images and icons must be SVG. An export that holds raster images is refused and the offending files are listed, so you can replace them and export again.
  • Frame thumbnails are raster renders Penpot rebuilds on its own, so the export leaves them out.
  • Import keeps the Penpot file id, so a round trip changes only what you drew.

Connect Penpot MCP

up enables MCP for the throwaway profile, issues its key and serves it behind the fixed /mcp/claude URL, so an agent needs the server added only once. Add it, then restart the agent:

claude mcp add --transport http penpot http://localhost:9001/mcp/claude

After later up runs, reconnect penpot from /mcp instead of restarting. While the stack is down, the server just shows as failed.

The MCP plugin runs inside the Penpot browser tab. Keep a file open in the workspace while the agent works; a hidden or unloaded tab stops MCP.

Release files for penpot-local-stack 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for penpot-local-stack 0.1.0
File Size Uploaded
penpot_local_stack-0.1.0.tar.gz 16.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for penpot-local-stack 0.1.0
File Interpreter ABI Platform
penpot_local_stack-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 29.0 kB

Release files / penpot_local_stack-0.1.0.tar.gz

Download URL penpot_local_stack-0.1.0.tar.gz
Size 16.0 kB
Tags Source
SHA-256 checksum
How to use checksums
a1262b69a10e0ada0e0dd42efebf9ea39aef584e044eb5e7311681bb05f0e6c3
BLAKE2b-256 checksum
How to use checksums
024fd1566757a24d657569bcd868309eccb7154ea3b674cba059236b38be561a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release files / penpot_local_stack-0.1.0-py3-none-any.whl

Download URL penpot_local_stack-0.1.0-py3-none-any.whl
Size 13.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
da43a2e88b4f12022e77d4be9e3da0a4b7f8074d4129ee33f7899b8e0892146b
BLAKE2b-256 checksum
How to use checksums
8c17a8dd2060e0dfa73afd25d97421c8b323962aba4bf316ffd03c06a581b953
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 20, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page