Element / Matrix
Element + serveur Matrix auto-hébergés — messagerie d’équipe avec chiffrement de bout en bout, voix, visio de groupe (Jitsi embarqué), et entrée SIP par téléphone. Fédération capable mais désactivée par défaut.
- Projet original : https://element.io/
- Remplace : Slack, Microsoft Teams, Signal (en usage pro), Zoom (pour les petits appels de groupe)
- Connexion (SSO) : Pré-câblé — la page de connexion affiche “Se connecter avec Keycloak” d’emblée, aucune étape post-déploiement.
Étapes de configuration
Section intitulée « Étapes de configuration »- Cliquez Deploy. Le premier démarrage prend ~3 min (Synapse génère ses clés de signature, postgres s’initialise, les composants Jitsi s’enregistrent).
- Ouvrez
element.<votre-domaine>— le client web Element s’ouvre. Cliquez Se connecter avec Keycloak. - Le premier utilisateur Keycloak arrive comme utilisateur Matrix normal. Pour le promouvoir admin du homeserver, ouvrez
synapseadmin.<votre-domaine>(réservé opérateur, protégé par le groupe admin Keycloak), trouvez l’utilisateur, et activez l’indicateur admin. - (Optionnel) Activez l’entrée SIP : remplissez
JIGASI_SIP_URI,JIGASI_SIP_PASSWORD,JIGASI_SIP_SERVERdans l’onglet Environment avec les identifiants de votre fournisseur SIP, puis redéployez. Sans ces valeurs, chat / voix / vidéo fonctionnent quand même — seule l’entrée par téléphone est désactivée. - (Optionnel) Ouvrez la fédération : éditez
FEDERATION_DOMAIN_WHITELISTdans l’onglet Environment (ex :"matrix.org","example.com") puis redéployez. Par défaut vide (pas de fédération — le homeserver ne parle qu’à lui-même).
Chiffrement de bout en bout
Section intitulée « Chiffrement de bout en bout »Les nouveaux messages directs et les nouveaux salons sur invitation sont chiffrés par défaut. Les salons publics restent en clair (le E2EE dans des salons publics nombreux dégrade la synchro mobile). Chaque utilisateur est invité à configurer le Coffre-fort de récupération lors de sa première connexion — une clé de 24 caractères qui lui permet de lire l’historique chiffré depuis un nouvel appareil. Perdre la clé verrouille l’utilisateur hors de ses anciens messages chiffrés ; sauvegardez-la comme un mot de passe maître.
Voix et vidéo
Section intitulée « Voix et vidéo »- Appels 1:1 utilisent le moteur d’appel Element/Matrix et le serveur TURN/STUN partagé
turn.<votre-domaine>pour relayer les médias en réseau restrictif. - Appels de groupe s’ouvrent dans un widget Jitsi embarqué sur
elementmeet.<votre-domaine>(l’instance Jitsi embarquée). Les appels ne quittent jamais votre serveur — pas de repli surmeet.jit.si. - Entrée SIP (jigasi) permet à un téléphone classique d’appeler un numéro SIP pour rejoindre un salon Jitsi. Activez en remplissant les variables d’environnement
JIGASI_SIP_*(voir étape 4 ci-dessus).
Applications mobiles
Section intitulée « Applications mobiles »Les apps iOS et Android d’Element se connectent directement à votre homeserver. Les utilisateurs touchent Utiliser un serveur personnalisé au premier lancement et saisissent matrix.<votre-domaine>. La connexion via Keycloak fonctionne dans l’app.
Variables d’environnement
Section intitulée « Variables d’environnement »Ces valeurs se trouvent dans l’onglet Environment du compose Dokploy. Les secrets aléatoires sont générés automatiquement au premier semi du template — vous n’avez pas à les générer vous-même.
| Variable | Valeur par défaut |
|---|---|
ELEMENT_HOSTNAME | element.yourdomain.com |
MATRIX_HOSTNAME | matrix.yourdomain.com |
ELEMENT_JITSI_HOSTNAME | elementmeet.yourdomain.com |
DB_PASSWORD | valeur aléatoire auto-générée |
SYNAPSE_REGISTRATION_SHARED_SECRET | valeur aléatoire auto-générée |
SYNAPSE_MACAROON_SECRET | valeur aléatoire auto-générée |
SYNAPSE_FORM_SECRET | valeur aléatoire auto-générée |
ALLOW_PUBLIC_REGISTRATION | false |
FEDERATION_DOMAIN_WHITELIST | (à définir avant déploiement) |
OIDC_BASE_URL | https://auth.yourdomain.com |
OIDC_CLIENT_ID | element |
OIDC_CLIENT_SECRET | <your-element_oidc_client_secret> |
VPS_PUBLIC_IP | <your-server-public-ip> |
TURN_HOSTNAME | turn.yourdomain.com |
TURN_STATIC_AUTH_SECRET | <your-turn_static_auth_secret> |
JITSI_JICOFO_AUTH_PASSWORD | <your-element_jitsi_jicofo_auth_password> |
JITSI_JICOFO_COMPONENT_SECRET | <your-element_jitsi_jicofo_component_secret> |
JITSI_JVB_AUTH_PASSWORD | <your-element_jitsi_jvb_auth_password> |
JIGASI_XMPP_PASSWORD | <your-element_jigasi_xmpp_password> |
JIGASI_SIP_URI | (à définir avant déploiement) |
JIGASI_SIP_PASSWORD | (à définir avant déploiement) |
JIGASI_SIP_SERVER | (à définir avant déploiement) |
- Service et port :
element-web:80 - Nom d’hôte :
element.yourdomain.com
Le nom d’hôte est attaché automatiquement au semi du template ; modifiez-le dans l’onglet Domains avant de cliquer Deploy si vous souhaitez autre chose.
Fichier compose
Section intitulée « Fichier compose »Pour référence — c’est ce que le template déploie. Ne collez ceci nulle part. Le compose est semé dans Dokploy automatiquement ; les ajustements côté client se font dans les onglets Environment et Domains (décrits plus haut), jamais dans le compose lui-même.
# Element / Matrix (Synapse) -- chat, voice, video, SIP, E2EE.## What's in the box:# - synapse : Matrix homeserver (chat + voice signalling + E2EE).# - element-web : Element web client.# - synapse-admin : admin UI for Synapse (user mgmt, room mgmt).# - postgres : Synapse's DB.# - prosody / jicofo / jvb / jitsi-web : bundled Jitsi for group video.# - jigasi : SIP <-> Jitsi gateway (dial-in from a SIP phone).## Shared infrastructure consumed (not in this compose):# - coturn at turn.<base>:5349 -- shared TURN/STUN for Matrix-native# 1:1 voice + Jitsi restrictive-network fallback. Same vault secret# as Nextcloud Talk + Rocket.Chat / Jitsi (see roles/coturn).# - Keycloak at auth.<base> -- OIDC IdP for Synapse + Element.## E2EE: encryption is on-by-default in Element for DMs and private# rooms (Synapse default since 1.0). Cross-signing + key backup work# out of the box; users opt in to key backup at first login.## Federation: disabled by default# (federation_domain_whitelist=[]) so the homeserver does not talk# to the public Matrix network without an explicit operator decision.# To open federation, edit /etc/synapse-template.yaml on the host or# set FEDERATION_DOMAIN_WHITELIST in the Environment tab.
x-synapse-image: &synapse_image image: matrixdotorg/synapse:v1.153.0
services: postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: synapse POSTGRES_USER: synapse POSTGRES_PASSWORD: ${DB_PASSWORD} # Synapse requires C collation on the DB. See # https://element-hq.github.io/synapse/latest/postgres.html POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=C --lc-ctype=C" volumes: - element-postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U synapse -d synapse"] interval: 10s start_period: 30s timeout: 5s retries: 5 labels: - "vps.auto-update=patch" networks: default: aliases: - postgres
synapse: <<: *synapse_image restart: unless-stopped # Custom entrypoint: render homeserver.yaml from /etc/synapse-template.yaml # by expanding ${ENV} placeholders, then exec synapse. Matrix-org's # stock start.py only templates a fixed subset of env vars; we need # arbitrary OIDC + TURN + federation config, so we drive it ourselves. entrypoint: ["python3", "/etc/catena-synapse-init.py"] environment: SYNAPSE_SERVER_NAME: ${MATRIX_HOSTNAME} SYNAPSE_REPORT_STATS: "no" MATRIX_HOSTNAME: ${MATRIX_HOSTNAME} ELEMENT_HOSTNAME: ${ELEMENT_HOSTNAME} DB_PASSWORD: ${DB_PASSWORD} SYNAPSE_REGISTRATION_SHARED_SECRET: ${SYNAPSE_REGISTRATION_SHARED_SECRET} SYNAPSE_MACAROON_SECRET: ${SYNAPSE_MACAROON_SECRET} SYNAPSE_FORM_SECRET: ${SYNAPSE_FORM_SECRET} OIDC_BASE_URL: ${OIDC_BASE_URL} OIDC_CLIENT_ID: ${OIDC_CLIENT_ID} OIDC_CLIENT_SECRET: ${OIDC_CLIENT_SECRET} TURN_HOSTNAME: ${TURN_HOSTNAME} TURN_STATIC_AUTH_SECRET: ${TURN_STATIC_AUTH_SECRET} ALLOW_PUBLIC_REGISTRATION: ${ALLOW_PUBLIC_REGISTRATION} FEDERATION_DOMAIN_WHITELIST: ${FEDERATION_DOMAIN_WHITELIST} JITSI_PREFERRED_DOMAIN: ${ELEMENT_JITSI_HOSTNAME} depends_on: postgres: condition: service_healthy volumes: - element-synapse-data:/data configs: - source: synapse-init target: /etc/catena-synapse-init.py mode: 0755 - source: synapse-homeserver-template target: /etc/synapse-template.yaml mode: 0644 - source: synapse-log-config target: /etc/synapse-log.config mode: 0644 labels: - "vps.auth.mode=public" - "vps.auth.oidc=true" - "vps.auth.groups=staff" - "vps.auth.oidc.redirect_uris=https://${MATRIX_HOSTNAME}/_synapse/client/oidc/callback" - "vps.auth.oidc.scopes=openid email profile groups" - "vps.auto-update=patch" networks: dokploy-network: aliases: - synapse default: {}
element-web: image: vectorim/element-web:v1.12.18 restart: unless-stopped environment: ELEMENT_WEB_PORT: "80" configs: - source: element-web-config target: /app/config.json mode: 0644 labels: - "vps.auth.mode=public" - "vps.auto-update=patch" networks: dokploy-network: aliases: - element-web default: {}
synapse-admin: image: awesometechnologies/synapse-admin:0.11.4 restart: unless-stopped environment: # Restricts the admin UI to managing THIS homeserver only. REACT_APP_SERVER: https://${MATRIX_HOSTNAME} depends_on: - synapse labels: # Admin tier: gated by oauth2-proxy admin instance. Operators # log in via Keycloak; only members of the operator group can # reach the UI. Mirrors the catena-admin gating posture. - "vps.auth.mode=admin" - "vps.auto-update=patch" networks: dokploy-network: aliases: - synapse-admin default: {}
# === JITSI BEGIN -- bundled on-server video conferencing ================= # Always-on, mirrors the Rocket.Chat pattern. Element's web client # opens video calls in an embedded Jitsi widget pointing at the # bundled instance at elementmeet.<base>. Uses the same shared coturn # at turn.<base>:5349 for restrictive-network fallback. # # Hostname is intentionally elementmeet.<base> (not meet.<base>) to # avoid collision with the Rocket.Chat bundled Jitsi when both # templates are deployed side-by-side.
prosody: image: jitsi/prosody:stable-10888 restart: unless-stopped expose: - "5222" - "5347" - "5280" environment: AUTH_TYPE: internal ENABLE_AUTH: "1" ENABLE_GUESTS: "1" GLOBAL_MODULES: "" GLOBAL_CONFIG: "" LDAP_URL: "" LDAP_BASE: "" XMPP_DOMAIN: meet.jitsi XMPP_AUTH_DOMAIN: auth.meet.jitsi XMPP_GUEST_DOMAIN: guest.meet.jitsi XMPP_MUC_DOMAIN: muc.meet.jitsi XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi XMPP_MODULES: "" XMPP_MUC_MODULES: "" XMPP_INTERNAL_MUC_MODULES: "" XMPP_RECORDER_DOMAIN: recorder.meet.jitsi JICOFO_AUTH_USER: focus JICOFO_AUTH_PASSWORD: ${JITSI_JICOFO_AUTH_PASSWORD} JICOFO_COMPONENT_SECRET: ${JITSI_JICOFO_COMPONENT_SECRET} JVB_AUTH_USER: jvb JVB_AUTH_PASSWORD: ${JITSI_JVB_AUTH_PASSWORD} # jigasi (SIP gateway) needs an XMPP account on prosody to join # rooms when a SIP call dials in. Auth user / password are # consumed by the jigasi service below. JIGASI_XMPP_USER: jigasi JIGASI_XMPP_PASSWORD: ${JIGASI_XMPP_PASSWORD} TZ: Etc/UTC labels: - "vps.auto-update=patch" networks: default: aliases: - meet.jitsi - auth.meet.jitsi - guest.meet.jitsi - muc.meet.jitsi - internal-muc.meet.jitsi - recorder.meet.jitsi
jicofo: image: jitsi/jicofo:stable-10888 restart: unless-stopped environment: XMPP_DOMAIN: meet.jitsi XMPP_AUTH_DOMAIN: auth.meet.jitsi XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi XMPP_MUC_DOMAIN: muc.meet.jitsi XMPP_SERVER: prosody JICOFO_COMPONENT_SECRET: ${JITSI_JICOFO_COMPONENT_SECRET} JICOFO_AUTH_USER: focus JICOFO_AUTH_PASSWORD: ${JITSI_JICOFO_AUTH_PASSWORD} # Tell jicofo about the SIP gateway so it routes dial-in # requests to jigasi instead of dropping them. JIGASI_SIP_URI: jigasi.meet.jitsi TZ: Etc/UTC depends_on: - prosody labels: - "vps.auto-update=patch" networks: - default
jvb: image: jitsi/jvb:stable-10888 restart: unless-stopped # Media UDP MUST be host-published. mode: host bypasses Swarm's # routing mesh so packets carry the real public source IP and # JVB's ICE candidates point at a routable address. Uses port # 10010 (not 10000) to avoid collision with the Rocket.Chat # bundled JVB on the same host. ports: - target: 10010 published: 10010 protocol: udp mode: host environment: XMPP_AUTH_DOMAIN: auth.meet.jitsi XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi XMPP_SERVER: prosody JVB_AUTH_USER: jvb JVB_AUTH_PASSWORD: ${JITSI_JVB_AUTH_PASSWORD} JVB_BREWERY_MUC: jvbbrewery JVB_PORT: "10010" JVB_ADVERTISE_IPS: ${VPS_PUBLIC_IP} JVB_TURN_HOST: ${TURN_HOSTNAME} JVB_TURN_PORT: "5349" JVB_TURN_TRANSPORT: tcp JVB_TURN_SECRET: ${TURN_STATIC_AUTH_SECRET} TZ: Etc/UTC depends_on: - prosody labels: - "vps.auto-update=patch" networks: - default
jitsi-web: image: jitsi/web:stable-10888 restart: unless-stopped expose: - "80" environment: ENABLE_LETSENCRYPT: "0" ENABLE_HTTP_REDIRECT: "0" ENABLE_HSTS: "0" DISABLE_HTTPS: "1" PUBLIC_URL: https://${ELEMENT_JITSI_HOSTNAME} XMPP_DOMAIN: meet.jitsi XMPP_AUTH_DOMAIN: auth.meet.jitsi XMPP_BOSH_URL_BASE: http://prosody:5280 XMPP_GUEST_DOMAIN: guest.meet.jitsi XMPP_MUC_DOMAIN: muc.meet.jitsi XMPP_RECORDER_DOMAIN: recorder.meet.jitsi TZ: Etc/UTC depends_on: - prosody labels: # Public by-link rooms; participants do not need an Element # account to join (Jitsi rooms are by-URL). - "vps.auth.mode=public" - "vps.auto-update=patch" networks: dokploy-network: aliases: - jitsi-web default: {}
jigasi: image: jitsi/jigasi:jigasi-1.1-412-ge9a3acc-1 restart: unless-stopped # SIP signalling. Port range is the standard Jigasi default; UDP # for RTP, TCP/UDP for SIP. mode: host so the SIP provider sees # the real VPS public IP in Via headers. ports: - target: 5060 published: 5060 protocol: udp mode: host - target: 5060 published: 5060 protocol: tcp mode: host environment: XMPP_SERVER: prosody XMPP_DOMAIN: meet.jitsi XMPP_AUTH_DOMAIN: auth.meet.jitsi XMPP_MUC_DOMAIN: muc.meet.jitsi XMPP_INTERNAL_MUC_DOMAIN: internal-muc.meet.jitsi XMPP_GUEST_DOMAIN: guest.meet.jitsi JIGASI_XMPP_USER: jigasi JIGASI_XMPP_PASSWORD: ${JIGASI_XMPP_PASSWORD} # SIP trunk credentials -- operator fills these in the # Environment tab once they have a SIP provider account # (Twilio Programmable Voice, OVH Telephony, Bandwidth, etc.). # Empty values leave jigasi unregistered; the rest of the # stack still works (chat, video, E2EE), only SIP dial-in is # off. To enable dial-in: fill these three vars and redeploy. JIGASI_SIP_URI: ${JIGASI_SIP_URI} JIGASI_SIP_PASSWORD: ${JIGASI_SIP_PASSWORD} JIGASI_SIP_SERVER: ${JIGASI_SIP_SERVER} JIGASI_SIP_PORT: "5060" JIGASI_SIP_TRANSPORT: UDP ENABLE_SIP_TRANSCRIBER: "0" ENABLE_SIP_VISUAL_NOTIFICATIONS: "1" TZ: Etc/UTC depends_on: - prosody labels: - "vps.auto-update=patch" networks: - default # === JITSI END ============================================================
configs: # Custom Synapse entrypoint -- expands ${ENV} placeholders in # /etc/synapse-template.yaml into /data/homeserver.yaml, then exec's # synapse. Necessary because matrix-org's stock start.py only # templates a fixed subset of env vars (SERVER_NAME, REPORT_STATS, # postgres) -- not OIDC, TURN, federation, presence, etc. synapse-init: content: | #!/usr/bin/env python3 import os, sys, pathlib, subprocess template = pathlib.Path("/etc/synapse-template.yaml").read_text() rendered = os.path.expandvars(template) out = pathlib.Path("/data/homeserver.yaml") out.parent.mkdir(parents=True, exist_ok=True) out.write_text(rendered) pathlib.Path("/etc/synapse-log.config").chmod(0o644) # First-boot: generate signing keys if missing. Idempotent -- # synapse refuses to overwrite existing keys. keys = pathlib.Path("/data/keys") keys.mkdir(parents=True, exist_ok=True) if not any(keys.glob("*.signing.key")): subprocess.check_call([ "python3", "-m", "synapse.app.homeserver", "--config-path=/data/homeserver.yaml", "--generate-keys", ]) os.execvp("python3", [ "python3", "-m", "synapse.app.homeserver", "--config-path=/data/homeserver.yaml", ])
synapse-homeserver-template: content: | # Templated by /etc/catena-synapse-init.py at container start. # ${VAR} placeholders are expanded from the container environment. server_name: "${MATRIX_HOSTNAME}" public_baseurl: "https://${MATRIX_HOSTNAME}/" pid_file: /data/homeserver.pid log_config: "/etc/synapse-log.config" report_stats: false signing_key_path: "/data/keys/signing.key" trusted_key_servers: []
listeners: - port: 8008 tls: false type: http x_forwarded: true bind_addresses: ["0.0.0.0"] resources: - names: [client, federation] compress: false
database: name: psycopg2 args: user: synapse password: "${DB_PASSWORD}" database: synapse host: postgres port: 5432 cp_min: 5 cp_max: 10
media_store_path: /data/media_store max_upload_size: 100M enable_registration: ${ALLOW_PUBLIC_REGISTRATION} enable_registration_without_verification: false registration_shared_secret: "${SYNAPSE_REGISTRATION_SHARED_SECRET}" macaroon_secret_key: "${SYNAPSE_MACAROON_SECRET}" form_secret: "${SYNAPSE_FORM_SECRET}"
# E2EE: encrypted DMs + private rooms by default. Public rooms # stay unencrypted (E2EE in large public rooms hurts UX). encryption_enabled_by_default_for_room_type: invite
# Federation: closed by default. FEDERATION_DOMAIN_WHITELIST="" means # the whitelist is the empty list -> no federation. To open # federation to specific peers, set the var to a comma-separated # list like "matrix.org,example.com" -- the YAML below uses an # explicit list rendered from the env var. federation_domain_whitelist: [${FEDERATION_DOMAIN_WHITELIST}]
# Presence / typing notifications: keep on for the team-chat UX. use_presence: true
# TURN via the shared coturn at turn.<base>. Same static-auth-secret # as Nextcloud Talk and Rocket.Chat / Jitsi. Synapse mints per-call # HMAC-SHA1 credentials (RFC 7635), same scheme JVB uses. turn_uris: - "turn:${TURN_HOSTNAME}:3478?transport=udp" - "turn:${TURN_HOSTNAME}:3478?transport=tcp" - "turns:${TURN_HOSTNAME}:5349?transport=tcp" turn_shared_secret: "${TURN_STATIC_AUTH_SECRET}" turn_user_lifetime: 86400000 turn_allow_guests: true
# OIDC -- Keycloak. The realm + client live in the operator's # Keycloak; ops/ converge mints the client (env_managed_keys # re-injects OIDC_CLIENT_SECRET on every converge). Users land # on Synapse's /_synapse/client/oidc/callback; Synapse maps the # `preferred_username` claim to the local part of the Matrix ID. oidc_providers: - idp_id: keycloak idp_name: "Keycloak" discover: true issuer: "${OIDC_BASE_URL}/realms/vps" client_id: "${OIDC_CLIENT_ID}" client_secret: "${OIDC_CLIENT_SECRET}" scopes: ["openid", "profile", "email"] user_mapping_provider: config: localpart_template: "{{ user.preferred_username }}" display_name_template: "{{ user.name }}" email_template: "{{ user.email }}" allow_existing_users: true
# Disable the legacy password login UI; users sign in via # Keycloak. (Bootstrap admin still works via registration-shared- # secret + register_new_matrix_user CLI when needed.) password_config: enabled: false
# Pre-populate the conference widget so Element's /jitsi command # and "video conference" button open elementmeet.<base> instead # of the public meet.jit.si fallback. app_service_config_files: []
synapse-log-config: content: | version: 1 formatters: precise: format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' handlers: console: class: logging.StreamHandler formatter: precise loggers: synapse.storage.SQL: level: INFO root: level: INFO handlers: [console] disable_existing_loggers: false
element-web-config: # Element's runtime config. Points the client at our Synapse # homeserver, pre-fills the Jitsi widget with the bundled instance # so video calls don't leak to meet.jit.si, and tightens defaults # (no telemetry, no integration manager). content: | { "default_server_config": { "m.homeserver": { "base_url": "https://${MATRIX_HOSTNAME}", "server_name": "${MATRIX_HOSTNAME}" } }, "brand": "Element", "disable_custom_urls": true, "disable_guests": true, "disable_login_language_selector": false, "disable_3pid_login": true, "default_country_code": "CA", "show_labs_settings": false, "default_federate": false, "default_theme": "light", "room_directory": { "servers": ["${MATRIX_HOSTNAME}"] }, "enable_presence_by_hs_url": { "https://${MATRIX_HOSTNAME}": true }, "settingDefaults": { "UIFeature.urlPreviews": true, "UIFeature.feedback": false, "UIFeature.registration": false, "UIFeature.passwordReset": false, "UIFeature.deactivate": false, "UIFeature.shareQrCode": true, "UIFeature.shareSocial": false, "UIFeature.identityServer": false, "UIFeature.thirdPartyId": false, "UIFeature.advancedSettings": true, "UIFeature.voip": true, "UIFeature.widgets": true }, "jitsi": { "preferred_domain": "${ELEMENT_JITSI_HOSTNAME}" }, "features": { "feature_element_call_video_rooms": true }, "element_call": { "url": "https://${ELEMENT_JITSI_HOSTNAME}", "use_exclusively": false }, "posthog": null, "analytics_owner": null, "privacy_policy_url": null }
volumes: element-postgres-data: element-synapse-data:
networks: dokploy-network: external: true