# MCP in a docker world

**URL:** <https://community.penpot.app/t/mcp-in-a-docker-world/10669>\
**Category:** Ask the community\
**Tags:** self-host, penpot-selfhosted, mcp\
**Created:** [June 18, 2026, 12:39pm UTC](https://community.penpot.app/t/mcp-in-a-docker-world/10669 "2026-06-18T12:39:26Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![rwam](https://dub1.discourse-cdn.com/flex017/user_avatar/community.penpot.app/rwam/32/4827_2.png) [@rwam](https://community.penpot.app/u/rwam)\
**Post date:** [June 18, 2026, 12:39pm UTC](https://community.penpot.app/t/mcp-in-a-docker-world/10669/1 "2026-06-18T12:39:26Z")

</div>

I followed [penpot/mcp at develop · penpot/penpot · GitHub](https://github.com/penpot/penpot/tree/develop/mcp#running-the-source-version-from-the-repository) to run the MCP-Server and the plugin locally and it works like a charm.

And I saw on [penpot/docker/images/docker-compose.yaml at develop · penpot/penpot · GitHub](https://github.com/penpot/penpot/blob/develop/docker/images/docker-compose.yaml) that a `penpot-mcp` service was added. So I’ve tried to check it out and updated my `docker-compose.yaml` but it doesn’t work. Neither can I install the MCP-Plugin nor can I connect the MCP-Server. I think there are missing some port mappings and environment variables. And it seems that this service is **server-only**.

So my question is: how can it be used? Is there something missing? Any help appreciated.

---

<div class="post-metadata">

**Author:** ![davidbarragan](https://dub1.discourse-cdn.com/flex017/user_avatar/community.penpot.app/davidbarragan/32/51_2.png) [@davidbarragan](https://community.penpot.app/u/davidbarragan)\
**Post date:** [June 22, 2026, 12:09pm UTC](https://community.penpot.app/t/mcp-in-a-docker-world/10669/2 "2026-06-22T12:09:12Z")

</div>

Hi, @rwam, the MCP runs on self-hosted instances in both Docker and Kubernetes (Helm).

Have you added the service?  
Have you enabled the MCP flag?  
Have you tried using the `docker-compose.yml` file from the repository to set it up locally?  
Can you share the contents of your docker-compose.yml file?

Best regards

---

<div class="post-metadata">

**Author:** ![rwam](https://dub1.discourse-cdn.com/flex017/user_avatar/community.penpot.app/rwam/32/4827_2.png) [@rwam](https://community.penpot.app/u/rwam)\
**Post date:** [June 23, 2026, 12:30pm UTC](https://community.penpot.app/t/mcp-in-a-docker-world/10669/3 "2026-06-23T12:30:59Z")

</div>

Hi @davidbarragan,

I use a customized `docker-compose.yaml` and adapted latest changes from the repository. So I’ve ended up here:

```auto
## Common flags:
# demo-users
# email-verification
# log-emails
# log-invitation-tokens
# login-with-github
# login-with-gitlab
# login-with-google
# login-with-ldap
# login-with-oidc
# login-with-password
# prepl-server
# registration
# secure-session-cookies
# smtp
# smtp-debug
# telemetry
# webhooks
##
## You can read more about all available flags and other
## environment variables here:
## https://help.penpot.app/technical-guide/configuration/#penpot-configuration
#
# WARNING: if you're exposing Penpot to the internet, you should remove the flags
# 'disable-secure-session-cookies' and 'disable-email-verification'
x-flags: &penpot-flags
  PENPOT_FLAGS: disable-email-verification enable-smtp enable-prepl-server disable-secure-session-cookies enable-mcp enable-registration enable-login-with-password

x-uri: &penpot-public-uri
  PENPOT_PUBLIC_URI: ${PENPOT_PUBLIC_URI:-http://penpot.our-internal-vm:9001}

x-body-size: &penpot-http-body-size
  # Max body size
  PENPOT_HTTP_SERVER_MAX_BODY_SIZE: 367001600
  # Deprecation warning: this variable is deprecated. Use PENPOT_HTTP_SERVER_MAX_BODY (defaults to 367001600)
  PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE: 367001600

## Penpot SECRET KEY. It serves as a master key from which other keys for subsystems
## (eg http sessions, or invitations) are derived.
##
## We recommend to use a trully randomly generated
## 512 bits base64 encoded string here. You can generate one with:
##
## python3 -c "import secrets; print(secrets.token_urlsafe(64))"
x-secret-key: &penpot-secret-key
  PENPOT_SECRET_KEY: A-Very-secret-key1_11

networks:
  penpot:

volumes:
  penpot_postgres_v15:
  penpot_assets:
  # penpot_traefik:

services:
  ## Traefik service declaration example. Consider using it if you are going to expose
  ## penpot to the internet, or a different host than `localhost`.

  # traefik:
  # image: traefik:v3.3
  # networks:
  # - penpot
  # command:
  # - "--api.insecure=true"
  # - "--entryPoints.web.address=:80"
  # - "--providers.docker=true"
  # - "--providers.docker.exposedbydefault=false"
  # - "--entryPoints.websecure.address=:443"
  # - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
  # - "--certificatesresolvers.letsencrypt.acme.email=<EMAIL_ADDRESS>"
  # - "--certificatesresolvers.letsencrypt.acme.storage=/traefik/acme.json"
  # volumes:
  # - "penpot_traefik:/traefik"
  # - "/var/run/docker.sock:/var/run/docker.sock"
  # ports:
  # - "80:80"
  # - "443:443"

  penpot-frontend:
    image: "penpotapp/frontend:latest"
    restart: always
    ports:
      - 9001:8080

    volumes:
      - penpot_assets:/opt/data/assets

    depends_on:
      - penpot-backend
      - penpot-exporter
      - penpot-mcp

    networks:
      - penpot

    # labels:
      # - "traefik.enable=true"

      # ## HTTPS: example of labels for the case where penpot will be exposed to the
      # ## internet with HTTPS using traefik.

      # - "traefik.http.routers.penpot-https.rule=Host(`<DOMAIN_NAME>`)"
      # - "traefik.http.routers.penpot-https.entrypoints=websecure"
      # - "traefik.http.routers.penpot-https.tls.certresolver=letsencrypt"
      # - "traefik.http.routers.penpot-https.tls=true"

    environment:
      << : [*penpot-flags, *penpot-http-body-size, *penpot-public-uri]
      # Set to "true" on hosts where IPv6 is disabled at kernel boot level.
      # PENPOT_DISABLE_IPV6_LISTEN: "true"

  penpot-backend:
    image: "penpotapp/backend:latest"
    restart: always

    volumes:
      - penpot_assets:/opt/data/assets

    depends_on:
      penpot-postgres:
        condition: service_healthy
      penpot-valkey:
        condition: service_healthy

    networks:
      - penpot

    ## Configuration environment variables for the backend container.

    environment:
      << : [*penpot-flags, *penpot-public-uri, *penpot-http-body-size, *penpot-secret-key]

      ## Database connection parameters. Don't touch them unless you are using custom
      ## postgresql connection parameters.

      PENPOT_DATABASE_URI: postgresql://penpot-postgres/penpot
      PENPOT_DATABASE_USERNAME: penpot
      PENPOT_DATABASE_PASSWORD: penpot

      ## Valkey (or previously redis) is used for the websockets notifications. Don't touch
      ## unless the valkey container has different parameters or different name.

      PENPOT_REDIS_URI: redis://penpot-valkey/0

      ## Default configuration for assets storage: using filesystem based with all files
      ## stored in a docker volume.

      PENPOT_OBJECTS_STORAGE_BACKEND: fs
      PENPOT_OBJECTS_STORAGE_FS_DIRECTORY: /opt/data/assets

      ## Also can be configured to to use a S3 compatible storage.

      # AWS_ACCESS_KEY_ID: <KEY_ID>
      # AWS_SECRET_ACCESS_KEY: <ACCESS_KEY>
      # PENPOT_OBJECTS_STORAGE_BACKEND: s3
      # PENPOT_OBJECTS_STORAGE_S3_ENDPOINT: <ENDPOINT>
      # PENPOT_OBJECTS_STORAGE_S3_BUCKET: <BUKET_NAME>

      ## Telemetry. When enabled, a periodical process will send anonymous data about this
      ## instance. Telemetry data will enable us to learn how the application is used,
      ## based on real scenarios. If you want to help us, please leave it enabled. You can
      ## audit what data we send with the code available on github.

      PENPOT_TELEMETRY_ENABLED: "true"
      PENPOT_TELEMETRY_REFERER: compose

      ## Example SMTP/Email configuration. By default, emails are sent to the mailcatch
      ## service, but for production usage it is recommended to setup a real SMTP
      ## provider. Emails are used to confirm user registrations & invitations. Look below
      ## how the mailcatch service is configured.

      PENPOT_SMTP_DEFAULT_FROM: Penpot <penpot@example.com>
      PENPOT_SMTP_DEFAULT_REPLY_TO: Penpot <penpot@example.com>
      PENPOT_SMTP_HOST: 1.2.3.4
      PENPOT_SMTP_PORT: 25
      PENPOT_SMTP_USERNAME:
      PENPOT_SMTP_PASSWORD:
      PENPOT_SMTP_TLS: "false"
      PENPOT_SMTP_SSL: "false"

  penpot-mcp:
    image: "penpotapp/mcp:${PENPOT_VERSION:-2.16}"
    restart: always
    networks:
      - penpot

  penpot-exporter:
    image: "penpotapp/exporter:latest"
    restart: always

    depends_on:
      penpot-valkey:
        condition: service_healthy

    networks:
      - penpot

    environment:
      << : [*penpot-secret-key]

      # Don't touch it; this uses an internal docker network to
      # communicate with the frontend.
      PENPOT_PUBLIC_URI: http://penpot-frontend:8080

      ## Valkey (or previously Redis) is used for the websockets notifications.
      PENPOT_REDIS_URI: redis://penpot-valkey/0

  penpot-postgres:
    image: "postgres:15"
    restart: always
    stop_signal: SIGINT

    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U penpot"]
      interval: 2s
      timeout: 10s
      retries: 5
      start_period: 2s

    volumes:
      - penpot_postgres_v15:/var/lib/postgresql/data

    networks:
      - penpot

    environment:
      - POSTGRES_INITDB_ARGS=--data-checksums
      - POSTGRES_DB=penpot
      - POSTGRES_USER=penpot
      - POSTGRES_PASSWORD=penpot

  penpot-valkey:
    image: valkey/valkey:8.1
    restart: always

    healthcheck:
      test: ["CMD-SHELL", "valkey-cli ping | grep PONG"]
      interval: 1s
      timeout: 3s
      retries: 5
      start_period: 3s

    networks:
      - penpot

    environment:
      # You can increase the max memory size if you have sufficient resources,
      # although this should not be necessary.
      - VALKEY_EXTRA_FLAGS=--maxmemory 128mb --maxmemory-policy volatile-lfu

  ## A mailcatch service, used as temporal SMTP server. You can access via HTTP to the
  ## port 1080 for read all emails the penpot platform has sent. Should be only used as a
  ## temporal solution while no real SMTP provider is configured.

  penpot-mailcatch:
    image: sj26/mailcatcher:latest
    restart: always
    expose:
      - '1025'
    ports:
      - "1080:1080"
    networks:
      - penpot

```

And everything works locally and on our VM but how can I install resp. serve the MCP plugin and connect to the MCP server?

---

<div class="post-metadata">

**Author:** ![davidbarragan](https://dub1.discourse-cdn.com/flex017/user_avatar/community.penpot.app/davidbarragan/32/51_2.png) [@davidbarragan](https://community.penpot.app/u/davidbarragan)\
**Post date:** [June 24, 2026, 9:02am UTC](https://community.penpot.app/t/mcp-in-a-docker-world/10669/4 "2026-06-24T09:02:15Z")

</div>

You just have to add the new service and the flag, the plugin to use it [is included](https://github.com/penpot/penpot/blob/develop/frontend/scripts/build#L62-L64) in the frontend image:

```auto
x-flags: &penpot-flags
  PENPOT_FLAGS: ... enable-mcp

...

services:
  ...

  penpot-frontend:
    image: "penpotapp/frontend:${PENPOT_VERSION:-2.16}"
    restart: always
    ports:
      - 9001:8080

    volumes:
      - penpot_assets:/opt/data/assets

    depends_on:
      - penpot-backend
      - penpot-exporter
      - penpot-mcp

    ...

  ...

  penpot-mcp:
    image: "penpotapp/mcp:${PENPOT_VERSION:-2.16}"
    restart: always
    networks:
      - penpot

  ...

```

You only need to install a plugin if you want to use your custom MCP (not the oficial image)

Regards.

---

<div class="post-metadata">

**Author:** ![rwam](https://dub1.discourse-cdn.com/flex017/user_avatar/community.penpot.app/rwam/32/4827_2.png) [@rwam](https://community.penpot.app/u/rwam)\
**Post date:** [June 24, 2026, 9:47am UTC](https://community.penpot.app/t/mcp-in-a-docker-world/10669/5 "2026-06-24T09:47:35Z")

</div>

Oh, sorry. my fault. I forgot to rebuild the containers. Everything seems to be fine with my YAML. So I have to run

```auto
docker compose -p penpot -f docker-compose.yaml up -d --build --force-recreate

```

And now I can see the MCP server on the integrations tab and the plugin on a penpot file.

---

<div class="post-metadata">

**Author:** ![davidbarragan](https://dub1.discourse-cdn.com/flex017/user_avatar/community.penpot.app/davidbarragan/32/51_2.png) [@davidbarragan](https://community.penpot.app/u/davidbarragan)\
**Post date:** [June 24, 2026, 10:30am UTC](https://community.penpot.app/t/mcp-in-a-docker-world/10669/6 "2026-06-24T10:30:17Z")

</div>

Great! We use `docker compose up -d --pull always --force-recreat` for upgrades.
