mirror of
https://github.com/penpot/penpot.git
synced 2026-09-30 15:56:17 +00:00
* 🐛 Allow invitation-based registration when disable-registration is set (#5178) Per documentation, disable-registration 'disables registration (still enabled for invitations only)'. Two bugs prevented this: 1. verify_token.clj: when processing an invitation token for a non-logged-in user with no member-id, the redirect included registration-disabled? in its condition, sending invited users to the login page instead of the register page. 2. auth.clj validate-register-attempt!: the registration-disabled check fired unconditionally before the invitation-token check, rejecting the actual register RPC even with a valid invitation. Fix: in verify_token.clj remove registration-disabled? from the redirect condition for new-user invitations. In auth.clj restructure the check as an if/else: with an invitation token, validate the token and allow registration; without one, enforce the flag as before. * 🐛 Allow registration with disabled public registration Allow valid team invitations to create new profiles when public registration is disabled, while keeping password login and invitation validation required. Add backend regression coverage for flag combinations and verify-token redirects, frontend route coverage, and configuration documentation. Closes #5178 AI-assisted-by: space-bunny-free * 🐛 Revalidate active invitation during registration Require a live, unexpired team invitation before using the registration exception, and recheck it before creating a profile. Reuse the same lookup in invitation token verification. Add regression tests for canceled and expired invitations, the registration race, and explicit redirect contracts. Update docs and backend auth guidance. AI-assisted-by: Space Bunny Free * 🐛 Lock and normalize invitation registration checks Lock active invitation rows during transactional registration and acceptance so cancellations cannot race with profile or membership creation. Normalize invitation emails before comparisons and database lookups. Add concurrency, email casing, and final flag regression tests. AI-assisted-by: Space Bunny Free --------- Co-authored-by: Sumit Ridhal <sridhal@redhat.com>
340 lines
13 KiB
Markdown
340 lines
13 KiB
Markdown
---
|
||
title: 1.2 Install with Docker
|
||
desc: This Penpot technical guide covers self-hosting, Docker installation, configuration, updates, backups, and proxy setup with NGINX and Caddy. Try Penpot!
|
||
---
|
||
|
||
<p class="advice">
|
||
Installing and maintaining a self-hosted Penpot instance requires some technical knowledge:
|
||
Docker and Docker Compose, basic DNS management, and proxy configuration.
|
||
If you're not comfortable with this stack, we encourage you to try
|
||
more straight-forward installations with <a href="https://help.penpot.app/technical-guide/getting-started/elestio/" target="_blank">Elestio</a>
|
||
or use the SAAS at <a href="https://design.penpot.app" targret="_blank">https://design.penpot.app</a>.
|
||
</p>
|
||
|
||
# Install with Docker
|
||
|
||
This section details everything you need to know to get Penpot up and running in
|
||
production environments using Docker. For this, we provide a series of *Dockerfiles* and a
|
||
*docker-compose* file that orchestrate all.
|
||
|
||
## Install Docker
|
||
|
||
To host a Penpot instance with Docker, it's necessary to have
|
||
<code class="language-bash">docker</code> and <code class="language-bash">docker compose</code>
|
||
installed. Check the comprehensive <a href="https://docs.docker.com/" target="_blank">official documentation</a>
|
||
to install and maintain docker.
|
||
|
||
## Start Penpot
|
||
|
||
As a first step you will need to obtain the <code class="language-bash">docker-compose.yaml</code> file. You can download it
|
||
<a
|
||
href="https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml"
|
||
target="_blank">from the Penpot repository</a>.
|
||
|
||
```bash
|
||
wget https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml
|
||
```
|
||
or
|
||
```bash
|
||
curl -o docker-compose.yaml https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml
|
||
```
|
||
|
||
Then simply launch composer:
|
||
|
||
```bash
|
||
docker compose -p penpot -f docker-compose.yaml up -d
|
||
```
|
||
|
||
At the end it will start listening on http://localhost:9001
|
||
|
||
<p class="advice">
|
||
If you don't change anything, by default this will use the latest image published in dockerhub.
|
||
</p>
|
||
|
||
If you want to have more control over the version (which is recommended), you can use the PENPOT_VERSION envvar in the common ways:
|
||
- setting the value in the .env file
|
||
- or passing the envvar in the command line
|
||
|
||
```bash
|
||
PENPOT_VERSION=2.4.3 docker compose -p penpot -f docker-compose.yaml up -d
|
||
```
|
||
|
||
## Stop Penpot
|
||
|
||
If you want to stop running Penpot, just type
|
||
|
||
```bash
|
||
docker compose -p penpot -f docker-compose.yaml down
|
||
```
|
||
|
||
## Configure Penpot with Docker
|
||
|
||
The configuration is defined using flags and environment variables in the <code class="language-bash">docker-compose.yaml</code>
|
||
file. The default downloaded file comes with the essential flags and variables already set,
|
||
and other ones commented out with some explanations.
|
||
|
||
You can find all configuration options in the [Configuration][1] section.
|
||
|
||
## Using the CLI for administrative tasks
|
||
|
||
Penpot provides a script (`manage.py`) with some administrative tasks to perform in the server.
|
||
|
||
**NOTE**: this script will only work with the <code class="language-bash">enable-prepl-server</code>
|
||
flag set in the docker-compose.yaml file. For older versions of docker-compose.yaml file,
|
||
this flag is set in the backend service.
|
||
|
||
For users who do not have a team invitation, if public registration is disabled, the
|
||
way to create a new user is with this script. Users with a valid and active team
|
||
invitation can register through the invitation link when password login is enabled.
|
||
The invitation must still exist and must not have expired.
|
||
|
||
```bash
|
||
docker exec -ti penpot-penpot-backend-1 python3 manage.py create-profile
|
||
```
|
||
|
||
or
|
||
|
||
```bash
|
||
docker exec -ti penpot-penpot-backend-1 python3 manage.py create-profile --skip-tutorial --skip-walkthrough
|
||
```
|
||
|
||
|
||
**NOTE:** the exact container name depends on your docker version and platform.
|
||
For example it could be <code class="language-bash">penpot-penpot-backend-1</code> or <code class="language-bash">penpot_penpot-backend-1</code>.
|
||
You can check the correct name executing <code class="language-bash">docker ps</code>.
|
||
|
||
## Update Penpot
|
||
|
||
To get the latest version of Penpot in your local installation, you just need to
|
||
execute:
|
||
|
||
```bash
|
||
docker compose -f docker-compose.yaml pull
|
||
```
|
||
|
||
This will fetch the latest images. When you do <code class="language-bash">docker compose up</code> again, the containers will be recreated with the latest version.
|
||
|
||
<p class="advice">
|
||
It is strongly recommended to update the Penpot version in small increments, rather than updating between two distant versions.
|
||
</p>
|
||
|
||
#### Upgrade to 2.18
|
||
|
||
This version deploys a new service, **`penpot-admin-console`**, in the official `docker-compose.yaml`
|
||
example. If you maintain your own compose file, you need to replicate the following changes manually:
|
||
|
||
**1. Add the `enable-admin-console` flag**
|
||
|
||
**NOTE:** Enabling the Admin Console is not required in this version, but it will be in a future release.
|
||
We recommend setting it up now to avoid a more complex upgrade later.
|
||
|
||
This step (the `enable-admin-console` flag) only applies to this version 2.18. In a future release, the
|
||
Admin Console will be enabled by default and this flag will be removed. If you're upgrading directly to a
|
||
version where the flag has already been removed, skip step 1 and go straight to steps 2–4, which remain
|
||
required.
|
||
|
||
Wherever you set `PENPOT_FLAGS` (frontend and backend):
|
||
|
||
```diff
|
||
- PENPOT_FLAGS: disable-email-verification enable-smtp enable-prepl-server disable-secure-session-cookies enable-mcp
|
||
+ PENPOT_FLAGS: disable-email-verification enable-smtp enable-prepl-server disable-secure-session-cookies enable-mcp enable-admin-console
|
||
```
|
||
|
||
**2. Add the new `penpot-admin-console` service**
|
||
|
||
```yaml
|
||
penpot-admin-console:
|
||
image: "penpotapp/admin-console:2.18"
|
||
restart: always
|
||
|
||
depends_on:
|
||
penpot-postgres:
|
||
condition: service_healthy
|
||
|
||
networks:
|
||
- penpot
|
||
|
||
environment:
|
||
PENPOT_PUBLIC_URI: http://localhost:9001
|
||
PENPOT_SECRET_KEY: change-this-insecure-key
|
||
PENPOT_DATABASE_URI: postgresql://penpot-postgres/penpot
|
||
PENPOT_DATABASE_USERNAME: penpot
|
||
PENPOT_DATABASE_PASSWORD: penpot
|
||
|
||
# Don't touch it; this uses an internal docker network to
|
||
# communicate with the frontend.
|
||
PENPOT_INTERNAL_URI: http://penpot-frontend:8080
|
||
```
|
||
> Use the same `PENPOT_PUBLIC_URI`, `PENPOT_SECRET_KEY`, and database credentials you already have configured for `penpot-backend`.
|
||
|
||
**3. Update `penpot-frontend`**
|
||
|
||
- Add `penpot-admin-console` to `depends_on`.
|
||
- Add the following environment variable:
|
||
|
||
```diff
|
||
environment:
|
||
PENPOT_FLAGS: disable-email-verification enable-smtp enable-prepl-server disable-secure-session-cookies enable-mcp enable-admin-console
|
||
PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 367001600
|
||
PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE: 367001600
|
||
PENPOT_PUBLIC_URI: http://localhost:9001
|
||
+
|
||
+ # Don't touch it; this uses an internal docker network to
|
||
+ # communicate with the admin-console.
|
||
+ PENPOT_ADMIN_CONSOLE_URI: http://penpot-admin-console:3000
|
||
```
|
||
|
||
**4. Update `penpot-backend`**
|
||
|
||
Add the same variable:
|
||
|
||
```diff
|
||
environment:
|
||
PENPOT_FLAGS: disable-email-verification enable-smtp enable-prepl-server disable-secure-session-cookies enable-mcp enable-admin-console
|
||
PENPOT_PUBLIC_URI: http://localhost:9001
|
||
PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 367001600
|
||
PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE: 367001600
|
||
PENPOT_SECRET_KEY: change-this-insecure-key
|
||
+
|
||
+ # Don't touch it; this uses an internal docker network to
|
||
+ # communicate with the admin-console.
|
||
+ PENPOT_ADMIN_CONSOLE_URI: http://penpot-admin-console:3000
|
||
```
|
||
|
||
#### Upgrade from version 1.x to 2.0
|
||
|
||
The migration to version 2.0, due to the incorporation of the new v2 components, includes
|
||
an additional process that runs automatically as soon as the application starts. If your
|
||
on-premises Penpot instance contains a significant amount of data (such as hundreds of
|
||
penpot files, especially those utilizing SVG components and assets extensively), this
|
||
process may take a few minutes.
|
||
|
||
In some cases, such as when the script encounters an error, it may be convenient to run
|
||
the process manually. To do this, you can disable the automatic migration process using
|
||
the <code class="language-bash">disable-v2-migration</code> flag in <code
|
||
class="language-bash">PENPOT_FLAGS</code> environment variable. You can then execute the
|
||
migration process manually with the following command:
|
||
|
||
```bash
|
||
docker exec -ti <container-name-or-id> ./run.sh app.migrations.v2
|
||
```
|
||
|
||
**IMPORTANT:** this script should be executed on passing from 1.19.x to 2.0.x. Executing
|
||
it on versions greater or equal to 2.1 of penpot will not work correctly. It is known that
|
||
this script is removed since 2.4.3
|
||
|
||
|
||
## Backup Penpot
|
||
|
||
Penpot uses <a href="https://docs.docker.com/storage/volumes" target="_blank">Docker
|
||
volumes</a> to store all persistent data. This allows you to delete and recreate
|
||
containers whenever you want without losing information.
|
||
|
||
This also means you need to do regular backups of the contents of the volumes. You cannot
|
||
directly copy the contents of the volume data folder. Docker provides you a <a
|
||
href="https://docs.docker.com/storage/volumes/#back-up-restore-or-migrate-data-volumes"
|
||
target="_blank">volume backup procedure</a>, that uses a temporary container to mount one
|
||
or more volumes, and copy their data to an archive file stored outside of the container.
|
||
|
||
If you use Docker Desktop, <a
|
||
href="https://www.docker.com/blog/back-up-and-share-docker-volumes-with-this-extension/"
|
||
target="_blank">there is an extension</a> that may ease the backup process.
|
||
|
||
If you use the default **docker compose** file, there are two volumes used: one for the
|
||
Postgres database and another one for the assets uploaded by your users (images and svg
|
||
clips). There may be more volumes if you enable other features, as explained in the file
|
||
itself.
|
||
|
||
## Configure the proxy and HTTPS
|
||
|
||
We strongly recommend to use Penpot under HTTPS/SSL, which will require specific server configurations for DNS and SSL certificates.
|
||
Besides, your host configuration needs to make a proxy to http://localhost:9001.
|
||
|
||
<p class="advice">
|
||
If you plan to serve Penpot under different domain than `localhost` without HTTPS,
|
||
you need to disable the `secure` flag on cookies, with the `disable-secure-session-cookies` flag.
|
||
This is a configuration NOT recommended for production environments; as some browser APIs do
|
||
not work properly under non-https environments, this unsecure configuration
|
||
may limit the usage of Penpot; as an example, the clipboard does not work with HTTP.
|
||
</p>
|
||
|
||
Below, you can see three examples with three different proxys:
|
||
|
||
### Example with NGINX
|
||
|
||
```bash
|
||
server {
|
||
listen 80;
|
||
server_name penpot.mycompany.com;
|
||
return 301 https://$host$request_uri;
|
||
}
|
||
|
||
server {
|
||
listen 443 ssl;
|
||
server_name penpot.mycompany.com;
|
||
|
||
# This value should be in sync with the corresponding in the docker-compose.yml
|
||
# PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 367001600
|
||
client_max_body_size 367001600;
|
||
|
||
# Logs: Configure your logs following the best practices inside your company
|
||
access_log /path/to/penpot.access.log;
|
||
error_log /path/to/penpot.error.log;
|
||
|
||
# TLS: Configure your TLS following the best practices inside your company
|
||
ssl_certificate /path/to/fullchain;
|
||
ssl_certificate_key /path/to/privkey;
|
||
|
||
# Websockets
|
||
location /ws/notifications {
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection 'upgrade';
|
||
proxy_pass http://localhost:9001/ws/notifications;
|
||
}
|
||
|
||
location /mcp/ws {
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection 'upgrade';
|
||
proxy_pass http://localhost:9001/mcp/ws;
|
||
}
|
||
|
||
# Proxy pass
|
||
location / {
|
||
proxy_set_header Host $http_host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Scheme $scheme;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_redirect off;
|
||
proxy_pass http://localhost:9001/;
|
||
}
|
||
}
|
||
```
|
||
|
||
For full documentation, go to the [official website][2]
|
||
|
||
### Example with CADDY SERVER
|
||
|
||
```bash
|
||
penpot.mycompany.com {
|
||
reverse_proxy :9001
|
||
tls /path/to/fullchain.pem /path/to/privkey.pem
|
||
log {
|
||
output file /path/to/penpot.log
|
||
}
|
||
}
|
||
```
|
||
|
||
For full documentation, go to the [official website][3]
|
||
|
||
### Example with TRAEFIK
|
||
|
||
In the [Penpot's docker-compose.yaml][4] file, there is a simple example with Traefik.
|
||
For full documentation, go to the [official website][5]
|
||
|
||
[1]: /technical-guide/configuration/
|
||
[2]: https://nginx.org/en/docs/index.html
|
||
[3]: https://caddyserver.com/docs/
|
||
[4]: https://github.com/penpot/penpot/blob/develop/docker/images/docker-compose.yaml
|
||
[5]: https://doc.traefik.io/traefik/
|