Auto-héberger AFFiNE avec Docker Compose
Déployez AFFiNE sur un VPS avec Docker Compose : quatre conteneurs, tags d’image figés, données, sauvegardes et ce que permettent réellement 2 Go de RAM.
Ce que vous obtenez en auto-hébergeant AFFiNE
L’auto-hébergement d’AFFiNE vous fournit un espace de travail de type Notion sur un serveur que vous contrôlez. Il s’exécute sous la forme de quatre conteneurs : l’application, une tâche de migration exécutée une seule fois, Postgres et Redis. La collaboration en temps réel est incluse, dans la limite des 10 sièges accordés par défaut à un espace de travail auto-hébergé. L’installation nécessite un seul fichier Compose et un seul fichier de configuration JSON. Les points à anticiper sont les tags d’image, l’organisation du stockage, la limite de mémoire et le proxy placé en amont.
AFFiNE regroupe un éditeur de documents et un canevas infini dans le même espace de travail. Une même page peut donc être consultée comme un document ou étendue comme un tableau blanc. Si vous hésitez encore sur le service à installer, consultez d’abord la comparaison des alternatives auto-hébergées à Notion. Ce guide part du principe que votre choix est fait et explique comment exécuter AFFiNE correctement, sans refaire cette comparaison.
Tout le contenu présenté ici a été vérifié à partir de la documentation d’auto-hébergement d’AFFiNE et des fichiers de release publiés le 8 août 2026. La dernière release stable à cette date était la version 0.27.3, publiée le 23 juillet 2026.
Ce que font réellement les quatre conteneurs
affine regroupe le serveur et le client web dans une seule image. Il écoute sur le port 3010.
affine_migration est une tâche exécutée une seule fois. Elle lance node ./scripts/self-host-predeploy.js, applique les migrations de la base de données, puis se termine. L’application déclare condition: service_completed_successfully pour cette tâche. Une migration qui se termine avec un code différent de zéro empêche donc complètement le démarrage de affine. Si l’interface web ne démarre pas, consultez d’abord le journal de cette tâche.
postgres contient vos documents, utilisateurs, espaces de travail et permissions. L’image fournie est pgvector/pgvector:pg16, c’est-à-dire PostgreSQL 16 standard avec l’extension pgvector compilée. pgvector ajoute à PostgreSQL un type de colonne vector. Cette représentation numérique sert à stocker les embeddings afin de pouvoir rechercher du texte par similarité de sens.
redis est une dépendance obligatoire : le serveur et la tâche de migration attendent tous deux que son health check soit validé avant de démarrer. Remarquez ce que le fichier compose fourni ne donne pas à Redis : un volume. Rien de ce qu’il contient ne survit à un docker compose down. Cela indique clairement qu’il ne contient aucune donnée vous appartenant et qu’il n’a pas besoin d’être sauvegardé.
Pourquoi l’image Postgres est pgvector et non l’image Postgres standard
Cette exigence vient du schéma d’AFFiNE, et non d’une préférence. Dans schema.prisma, la source de données déclare extensions = [pgvector(map: "vector")], et quatre tables contiennent une colonne embedding typée vector(1024). Le job de migration crée ces tables, que vous activiez ou non les fonctionnalités d’IA. L’extension doit donc déjà exister dans la base de données pour que la migration puisse se terminer. Si vous remplacez l’image par postgres:16, l’extension disparaît. La migration ne peut plus créer ces colonnes, et le serveur reste en attente d’un job qui a échoué.
AFFiNE est passé à l’image pgvector avec la version 0.21. Sur une installation plus ancienne, modifier la ligne de l’image ne suffit pas pour effectuer la mise à niveau. Consultez donc la page de mise à niveau de la documentation d’auto-hébergement d’AFFiNE avant de télécharger quoi que ce soit.
Autre point concernant ce tag. pg16 correspond à Postgres 16. La version majeure de Postgres ne se modifie pas comme un simple numéro. Si vous la remplacez par pg17 en conservant un répertoire de données existant, Postgres refuse de démarrer et affiche une ligne telle que The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17 dans docker compose logs postgres. Le changement de version majeure nécessite un dump, puis une restauration dans un nouveau répertoire de données.
De combien de CPU et de RAM AFFiNE a-t-il besoin en auto-hébergement
La page des prérequis d’AFFiNE demande au moins 4 cœurs CPU et 2 GB de RAM. Elle recommande 4 GB de mémoire lorsque vos documents dépassent 10,000 mots. La même page indique où va la mémoire : le système de synchronisation et la fusion des documents. Elle donne un chiffre à retenir : la fusion d’un document comportant 10,000 modifications peut atteindre 1 GB.
Comparez maintenant ces besoins à une offre de 2 GB utilisée par deux personnes. La moyenne ne pose pas de problème. Postgres et le processus Node restent sous la limite, avec une marge disponible. Le problème vient du pic. Une seule fusion volumineuse peut demander 1 GB en plus de toute la mémoire déjà utilisée. Sur une machine de 2 GB sans swap, le tueur OOM (out of memory) du kernel répond à cette demande en arrêtant le processus le plus gourmand, à savoir le serveur AFFiNE.
Votre collègue ne voit pas d’erreur. La page se recharge, car restart: unless-stopped relance le conteneur en quelques secondes. Ne faites pas de suppositions : vérifiez-le :
docker inspect affine_server --format '{{.State.OOMKilled}} {{.RestartCount}}'
sudo dmesg -T | grep -i -E 'out of memory|killed process'true dans la première commande, ou une ligne Killed process mentionnant node dans la seconde, signifie que vous avez manqué de mémoire, et non découvert un bug. Corrigez le problème des deux côtés. Commencez par ajouter du swap afin qu’un pic ralentisse le système au lieu de provoquer un arrêt :
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -hfree -h doit maintenant indiquer un total de swap de 2.0Gi. Le swap ne rend pas AFFiNE plus rapide, et ce n’est pas son objectif. Il transforme un pic d’une seconde en une seconde de ralentissement, au lieu de provoquer l’arrêt du conteneur. L’autre partie de la correction consiste à empêcher Postgres d’étendre son cache dans l’espace dont l’application a besoin pendant la fusion. C’est précisément le rôle des limites de mémoire sur un service Compose.
Le stockage est beaucoup plus facile à prévoir. Voici les chiffres publiés par AFFiNE sur cette même page :
The data behind this chart
[
{
"label": "Server install",
"gb": 1.5
},
{
"label": "Postgres per 1,000 docs",
"gb": 0.1
},
{
"label": "Blob store per 1,000 uploads",
"gb": 10
}
]L’installation du serveur occupe 1.5 GB. Mille documents d’environ mille mots chacun ajoutent 0.1 GB de données Postgres, ce qui est presque négligeable. Mille fichiers importés ajoutent 10 GB : c’est l’essentiel. Il s’agit de chiffres de planification publiés, et non de mesures effectuées sur une instance en fonctionnement. Considérez-les donc comme une tendance, et non comme une garantie. Ce qui compte, c’est la tendance : votre base de données reste petite, tandis que vos fichiers importés déterminent l’espace disque nécessaire.
Rédigez vous-même le fichier Compose, avec des tags figés
La procédure d’installation documentée télécharge un fichier prêt à l’emploi avec curl -L -o docker-compose.yml https://github.com/toeverything/AFFiNE/releases/latest/download/docker-compose.yml. Cela fonctionne. Un détail mérite toutefois votre attention avant de vous y fier : au 8 août 2026, le fichier joint à la release 0.27.3 lit encore ses chemins dans un fichier .env, avec ${UPLOAD_LOCATION}, ${CONFIG_LOCATION} et ${DB_DATA_LOCATION}, tandis que la page de référence de la documentation présente une structure plus récente qui regroupe tout sous ./data et ne nécessite aucun .env. Les deux versions sont valides. Rédiger vous-même le fichier règle la question, et vous devez de toute façon le modifier pour figer les images et définir un mot de passe de base de données.
mkdir -p ~/affine/config ~/affine/data
cd ~/affine
printf 'DB_PASSWORD=%s\n' "$(openssl rand -hex 24)" > .env
chmod 600 .envCompose lit automatiquement .env dans le répertoire du projet et remplace ${DB_PASSWORD} pour vous. Le mot de passe n’apparaît donc jamais dans le fichier que vous pourriez publier dans un fil de support. Conservez cette habitude pour toutes les stacks que vous utilisez ; le raisonnement est expliqué dans ne pas laisser de secrets dans le fichier Compose.
Écrivez maintenant ~/affine/docker-compose.yml :
name: affine
services:
affine:
image: ghcr.io/toeverything/affine:stable
container_name: affine_server
ports:
- '127.0.0.1:3010:3010'
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
affine_migration:
condition: service_completed_successfully
volumes:
- ./data/storage:/root/.affine/storage
- ./config:/root/.affine/config
environment:
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgresql://affine:${DB_PASSWORD}@postgres:5432/affine
- AFFINE_INDEXER_ENABLED=false
restart: unless-stopped
affine_migration:
image: ghcr.io/toeverything/affine:stable
container_name: affine_migration_job
command: ['sh', '-c', 'node ./scripts/self-host-predeploy.js']
volumes:
- ./data/storage:/root/.affine/storage
- ./config:/root/.affine/config
environment:
- REDIS_SERVER_HOST=redis
- DATABASE_URL=postgresql://affine:${DB_PASSWORD}@postgres:5432/affine
- AFFINE_INDEXER_ENABLED=false
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
redis:
image: redis:8-alpine
container_name: affine_redis
healthcheck:
test: ['CMD', 'redis-cli', '--raw', 'incr', 'ping']
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
postgres:
image: pgvector/pgvector:pg16
container_name: affine_postgres
volumes:
- ./data/postgres:/var/lib/postgresql/data
environment:
POSTGRES_USER: affine
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: affine
POSTGRES_INITDB_ARGS: '--data-checksums'
healthcheck:
test: ['CMD', 'pg_isready', '-U', 'affine', '-d', 'affine']
interval: 10s
timeout: 5s
retries: 5
restart: unless-stoppedVoici quatre différences par rapport au fichier fourni en amont. Chacune a une raison.
127.0.0.1:3010:3010publie le port uniquement sur l’adresse loopback. Rien à l’extérieur du serveur ne peut donc atteindre AFFiNE tant que vous n’avez pas choisi comment l’exposer. Le fichier'3010:3010'fourni en amont se lie à toutes les interfaces, dont l’interface publique sur la plupart des images VPS.POSTGRES_HOST_AUTH_METHOD: trustest supprimé et un mot de passe est défini à la place. L’authentification trust accepte toute connexion à cette base de données en tant qu’utilisateuraffine, sans mot de passe. Elle est limitée au réseau Compose privé, ce qui convient jusqu’au jour où vous ajoutez un autre conteneur à ce réseau ou publiez le port 5432 pour effectuer un diagnostic.redis:8-alpineremplace un simpleredis, qui pointe verslatest. En août 2026, il s’agit de Redis 8. Le tag figé conserve donc la version majeure que vous avez testée et empêche l’arrivée de Redis 9 pendant unedocker compose pullsans rapport.pgvector/pgvector:pg16reste exactement défini comme en amont, pour la raison indiquée plus haut.
POSTGRES_PASSWORD est lu uniquement lorsque Postgres crée son répertoire de données pour la première fois. Sur une instance qui existe déjà, définissez le mot de passe avec docker compose exec postgres psql -U affine -c "ALTER USER affine WITH PASSWORD 'yourpassword'", puis mettez DATABASE_URL à jour en conséquence.
La configuration se trouve dans config/config.json
AFFiNE lit ses paramètres dans config/config.json, qui correspond au répertoire monté sur /root/.affine/config. Ce fichier n’est pas créé automatiquement. Vous devez donc le créer avant le premier démarrage. Ouvrez ~/affine/config/config.json dans un éditeur et donnez-lui le contenu suivant, en remplaçant l’exemple par votre propre domaine :
{
"$schema": "https://github.com/toeverything/affine/releases/latest/download/config.schema.json",
"server": {
"name": "Team workspace",
"externalUrl": "https://affine.example.com"
},
"copilot": {
"enabled": false,
"byok": {
"enabled": false
}
}
}server.externalUrl doit être l’adresse que vos utilisateurs ouvrent réellement dans un navigateur. AFFiNE construit les liens de partage et les invitations aux espaces de travail à partir de cette valeur. Si elle reste définie sur http://localhost:3010, une invitation envoyée redirige le destinataire vers sa propre machine et échoue. Définissez cette valeur sur l’adresse HTTPS publique avant le premier démarrage. Le fichier de configuration et le panneau d’administration utiliseront ainsi la même adresse.
copilot contrôle les fonctionnalités d’IA. copilot.byok.enabled active le mode « bring your own key », qui permet au propriétaire d’un espace de travail de saisir sa propre clé de fournisseur de modèles dans les paramètres de l’espace de travail. L’auto-hébergement d’AFFiNE n’inclut pas d’abonnement à un service d’IA. Laissez les deux paramètres false si vous ne souhaitez pas utiliser ces fonctionnalités.
Démarrez la stack :
docker compose up -d
docker compose psdocker compose ps doit indiquer que affine_postgres et affine_redis sont sains, que affine_server est en cours d’exécution et que affine_migration_job possède l’état exited (0). Tout autre code de sortie du job de migration doit être examiné. Son journal indique l’étape qui s’est arrêtée :
docker compose logs affine_migrationÉpinglez l’image avant de l’oublier
stable est un tag mobile. Le workflow de publication d’AFFiNE associe plusieurs tags à chaque build stable. Deux d’entre eux nous intéressent ici : stable, réassigné à chaque publication, et stable- suivi du hash court Git, qui ne change pas. Si vous conservez stable, un docker compose pull exécuté six mois plus tard récupérera une autre image et appliquera ses migrations à votre base de données à un moment que vous n’avez pas choisi. Épinglez l’image exacte que vous avez testée :
docker compose pull
docker image inspect ghcr.io/toeverything/affine:stable --format '{{index .RepoDigests 0}}'Cette commande affiche une ligne comme ghcr.io/toeverything/affine@sha256: suivie d’un long hash. Collez la chaîne complète dans la ligne image: de **affine et affine_migration. Ces deux valeurs doivent toujours être identiques, car il s’agit de la même image utilisée dans deux rôles. Une différence signifie que la base de données est migrée vers un schéma, puis utilisée avec un autre. Une mise à niveau devient alors une modification volontaire plutôt qu’une surprise : changez le digest, effectuez une sauvegarde, docker compose pull, docker compose up -d.
Créer le compte administrateur avant toute autre personne
Ouvrez /admin sur une instance neuve. AFFiNE vous redirige vers une page de création de compte, car le serveur n’a encore aucun administrateur. Ce parcours ne demande ni code d’invitation ni jeton d’installation. La première personne qui charge cette page devient l’administrateur de votre serveur. Le port doit donc rester fermé jusqu’à la création de votre compte.
C’est pourquoi le fichier Compose ci-dessus lie le service à 127.0.0.1. Accédez-y via un tunnel SSH depuis votre propre machine :
ssh -L 3010:127.0.0.1:3010 you@your-server-ipLaissez ce tunnel ouvert et ouvrez http://127.0.0.1:3010/admin dans votre navigateur local. Créez votre compte et connectez-vous, puis fermez le tunnel. Vous pouvez seulement maintenant exposer l’instance avec un nom public. Le même problème de concurrence existe dans d’autres applications auto-hébergées. Il est plus grave lorsque la première connexion crée une passkey liée au nom d’hôte. C’est pourquoi TLS et le domaine définitif doivent être configurés avant la création du premier compte lorsque vous auto-hébergez openGym.
Emplacement des données d’AFFiNE
Trois chemins contiennent toutes les données. Ils se trouvent tous dans le répertoire que vous avez créé.
./data/postgresest le répertoire de données Postgres : documents, utilisateurs, espaces de travail et permissions../data/storageest monté sur/root/.affine/storagedans le conteneur et contient tous les fichiers téléversés../configest monté sur/root/.affine/configet contientconfig.json.
Le projet amont utilise ici des bind mounts plutôt que des named volumes. Ce choix est délibéré : vous pouvez archiver et copier ces chemins avec des commandes classiques, sans demander à Docker où il les a placés. En contrepartie, la gestion de la propriété des fichiers sur l’hôte vous incombe. C’est le compromis présenté dans bind mounts et named volumes.
Comment sauvegarder AFFiNE
Deux éléments doivent être sauvegardés, et ils ne le sont pas de la même manière. La base de données est un serveur actif. Copier ses fichiers pendant son fonctionnement produit une copie corrompue. Effectuez plutôt un dump :
mkdir -p ~/affine/backup
cd ~/affine
docker compose exec -T postgres pg_dump --format c --username affine affine \
> backup/affine-$(date +%F).dump
ls -lh backup/Le dump s’exécute dans le conteneur via son socket local. Le mot de passe n’est donc pas demandé. Vérifiez la taille dans la sortie de ls. Un fichier de quelques centaines d’octets indique que le dump a échoué alors que le shell a quand même créé le fichier. C’est un problème que l’on découvre six mois plus tard. -T est également important : sans cette option, Compose peut allouer un terminal et corrompre le flux binaire.
Les fichiers importés sont de simples fichiers. Archivez-les donc avec tar :
tar czf backup/storage-$(date +%F).tgz -C data storage
cp config/config.json backup/config-$(date +%F).jsonConservez config.json manuellement dans votre sauvegarde. En août 2026, la documentation d’AFFiNE indique toujours que l’export de la configuration depuis le panneau d’administration n’est pas implémenté. Le fichier présent sur le disque est donc la seule copie de vos paramètres. Copiez les trois fichiers hors du serveur. Une sauvegarde stockée sur le même disque que les données qu’elle protège n’est pas une sauvegarde. Cette séparation entre un dump de la base de données et une archive tar du répertoire des uploads est le modèle à reprendre pour chaque autre conteneur stateful que vous exécutez. C’est également ce qui protège l’historique des conversations et les pièces jointes lorsque vous hébergez vous-même Chatwoot comme outil de support.
Restaurer les données : attention à un piège dans la procédure publiée
Lisez la procédure officielle de restauration avant d’en avoir besoin, et lisez-la attentivement. Dans sa version publiée en août 2026, elle copie un fichier nommé affine.backup dans le conteneur, puis effectue la restauration depuis ./pg.backup. Il s’agit de deux noms différents. Elle supprime également un répertoire ./postgres, alors que le fichier compose actuel conserve ses données dans ./data/postgres. Utilisez les chemins réellement employés dans votre installation plutôt que ceux de l’extrait. Voici la séquence correspondant à l’organisation utilisée dans ce guide :
cd ~/affine
docker compose down
sudo mv data/postgres data/postgres.old
docker compose up -d postgres
docker compose cp backup/affine-2026-08-08.dump postgres:/tmp/affine.dump
docker compose exec postgres pg_restore --format c --username affine \
--dbname affine --verbose /tmp/affine.dump
docker compose up -dNotez l’utilisation de mv, et non de rm. Restaurer par-dessus une base de données dont vous n’avez pas conservé de copie transforme une simple mauvaise commande en perte totale de données. Mettre l’ancien répertoire de côté ne coûte rien. Restaurez également les fichiers importés avec tar xzf backup/storage-2026-08-08.tgz -C data, sinon tous les documents s’afficheront avec des pièces jointes manquantes. Connectez-vous ensuite et ouvrez un document contenant une image. C’est le test à effectuer. Une restauration que vous n’avez pas ouverte dans un navigateur est un fichier, pas une sauvegarde.
Mettre AFFiNE derrière un proxy déjà en service
AFFiNE utilise WebSocket, et ce fonctionnement est obligatoire. La documentation est claire : WebSocket est à la base de la synchronisation et de la collaboration dans AFFiNE. Un proxy qui ne met pas ces connexions à niveau vous donne un espace de travail dans lequel les modifications cessent discrètement d’être synchronisées. La page se charge, la connexion fonctionne, mais une modification effectuée dans un navigateur n’atteint jamais l’autre. Dans les outils de développement de votre navigateur, ouvrez l’onglet Network et filtrez sur WS. Une connexion qui s’ouvre et se ferme en boucle indique que le proxy ne transmet pas la mise à niveau.
Si vous utilisez déjà Traefik pour d’autres conteneurs, AFFiNE s’y ajoute comme un service normal. Supprimez le bloc ports: du service affine, puis ajoutez :
networks:
- default
- proxy
labels:
- 'traefik.enable=true'
- 'traefik.docker.network=proxy'
- 'traefik.http.routers.affine.rule=Host(`affine.example.com`)'
- 'traefik.http.routers.affine.entrypoints=websecure'
- 'traefik.http.routers.affine.tls.certresolver=letsencrypt'
- 'traefik.http.services.affine.loadbalancer.server.port=3010'Et en bas du fichier, à côté de services: :
networks:
proxy:
external: trueLe nom du certificate resolver doit correspondre à celui défini dans la configuration de Traefik. loadbalancer.server.port désigne le port du conteneur 3010, jamais un port de l’hôte. Traefik proxyfie les connexions WebSocket sans configuration supplémentaire. Vous n’avez donc rien d’autre à ajouter. Si le reste de votre stack est déjà derrière Authentik pour l’authentification unique, un middleware forward auth sur ce router contrôlera l’accès navigateur à AFFiNE. Désactivez-le toutefois jusqu’à ce que vous ayez testé l’application desktop, qui ne dispose d’aucune session navigateur et échouera simplement à se synchroniser. La configuration de plusieurs applications derrière une même instance est présentée dans un Traefik unique devant plusieurs applications.
Avec nginx, vous devez demander explicitement la mise à niveau :
location / {
proxy_pass http://127.0.0.1:3010;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 100m;
}client_max_body_size vaut 1 MB par défaut dans nginx. Sans cette ligne, tout upload de plus d’une petite photo échoue avec un statut 413, et rien n’apparaît dans les journaux d’AFFiNE, car la requête n’est jamais arrivée. Caddy nécessite une seule ligne, reverse_proxy http://127.0.0.1:3010, et gère lui-même les certificats et les mises à niveau WebSocket.
Ce que l’instance auto-hébergée n’inclut pas
Soyez réaliste à ce sujet avant de faire migrer une équipe.
La collaboration en temps réel est disponible. C’est la fonctionnalité sur laquelle porte la plupart des recommandations de dimensionnement, car la documentation d’AFFiNE attribue la consommation mémoire au système de synchronisation et à la fusion des documents. La modification hors ligne explique pourquoi de nombreuses personnes veulent un outil local-first. L’application de bureau peut ajouter votre serveur auto-hébergé à sa liste d’espaces de travail et s’y connecter. Testez le fonctionnement hors ligne exact dont votre équipe dépend avant de vous engager : modifiez un document dans l’application de bureau avec le réseau désactivé, reconnectez-vous, puis vérifiez le résultat sur un deuxième appareil. Une liste de fonctionnalités ne constitue pas une preuve, y compris celle-ci.
La recherche plein texte côté serveur est désactivée dans le fichier compose fourni. La variable AFFINE_INDEXER_ENABLED=false y est définie sur le serveur et sur le job de migration. Pour l’activer, vous devez ajouter un conteneur Manticore Search. Cela ajoute un cinquième service et augmente la consommation mémoire. Sur une machine dotée de 2 GB de mémoire, c’est la modification qui vous fait dépasser la limite. La recherche dans le client continue de fonctionner sur l’espace de travail ouvert.
Deux limites sont à connaître avant d’inviter des utilisateurs. Un espace de travail auto-hébergé peut accueillir au maximum 10 utilisateurs. Au-delà, vous devez obtenir une licence Team auprès d’AFFiNE. La documentation décrit le stockage illimité des blobs et la taille illimitée des blobs pour les instances auto-hébergées comme des fonctionnalités prévues, mais pas encore entièrement implémentées, selon la vérification effectuée en August 2026. Aucune de ces limites n’est importante pour un foyer ou une petite équipe. Elles le deviennent si vous prévoyez de faire migrer 40 personnes.
Mises à niveau
Lisez d’abord les notes de version, en particulier pour une mise à niveau mineure telle que 0.26 vers 0.27, car des changements incompatibles peuvent être introduits. Sauvegardez la base de données et le répertoire de stockage avant toute intervention, car la tâche de migration modifie votre schéma au prochain démarrage et il n’existe aucune procédure d’annulation. Modifiez ensuite le digest épinglé, exécutez docker compose pull puis docker compose up -d, et surveillez docker compose logs -f affine_migration jusqu’à son arrêt normal. docker image prune supprime ensuite les anciennes couches. À noter pour les installations très anciennes : à partir de la version 0.23.0, le nom de l’image est passé de affine-graphql à affine. Un fichier compose antérieur à cette version doit donc avoir ses lignes image réécrites avant qu’un pull puisse trouver l’image.
FAQ
Pourquoi le conteneur AFFiNE ne démarre-t-il jamais ?
Le service affine déclare condition: service_completed_successfully pour le job affine_migration. Si la migration se termine avec un code différent de 0, le serveur ne démarre jamais et aucune interface web ne s’affiche. Exécutez docker compose logs affine_migration pour voir à quelle étape le processus s’est arrêté. Sur un fichier Compose modifié manuellement, la cause la plus fréquente est l’utilisation d’une image postgres standard à la place de pgvector/pgvector:pg16. Le schéma AFFiNE déclare en effet l’extension pgvector et crée des tables avec des colonnes vector(1024) que Postgres standard ne peut pas créer.
De quelle quantité de RAM AFFiNE a-t-il besoin en auto-hébergement ?
La page des prérequis d’AFFiNE demande au moins 4 cœurs CPU et 2 GB de RAM. Cette quantité passe à 4 GB lorsque les documents dépassent 10,000 mots. Elle précise aussi que la fusion d’un document comportant 10,000 modifications peut utiliser jusqu’à 1 GB. Sur un serveur doté de 2 GB, c’est ce pic qui provoque l’arrêt, pas la charge au repos : le tueur de processus « out of memory » du kernel arrête le processus AFFiNE, puis restart: unless-stopped le redémarre. Les utilisateurs voient donc un rechargement de la page plutôt qu’une erreur. Confirmez-le avec docker inspect affine_server --format '{{.State.OOMKilled}}' et sudo dmesg -T | grep -i 'out of memory', puis ajoutez un fichier swap de 2 GB afin qu’un pic ralentisse le système au lieu de provoquer un arrêt.
Où AFFiNE stocke-t-il mes données et que dois-je sauvegarder ?
Trois chemins sous votre répertoire Compose contiennent toutes les données : ./data/postgres pour la base de données, ./data/storage pour les fichiers importés et ./config pour config.json. Sauvegardez la base de données avec docker compose exec -T postgres pg_dump --format c --username affine affine > affine.dump plutôt qu’en copiant les fichiers, car une instance Postgres en fonctionnement ne peut pas être copiée sans risque. Archivez ./data/storage avec tar pour les fichiers importés et conservez manuellement une copie de config.json, car l’export de la configuration depuis le panneau d’administration est indiqué comme non implémenté en août 2026.
La collaboration en temps réel fonctionne-t-elle avec un AFFiNE auto-hébergé ?
Oui. Aucune activation n’est nécessaire. La seule exigence concerne votre reverse proxy, car la synchronisation utilise des connexions WebSocket. Avec nginx, cela signifie proxy_http_version 1.1 ainsi que les en-têtes Upgrade et Connection: upgrade. Traefik et Caddy transmettent ces connexions sans configuration supplémentaire. Si le proxy ne les fait pas basculer vers WebSocket, l’espace de travail se charge et l’utilisateur peut se connecter normalement, mais les modifications effectuées dans un navigateur n’apparaissent jamais dans un autre.
Puis-je utiliser AFFiNE avec une image Postgres standard ?
Non. Le schema.prisma d’AFFiNE déclare extensions = [pgvector(map: "vector")] et définit quatre tables comportant une colonne embedding de type vector(1024). Le job de migration crée ces tables même lorsque les fonctionnalités d’IA sont désactivées. Utilisez pgvector/pgvector:pg16, qui est basé sur Postgres 16 avec cette extension compilée. Si vous connectez plutôt AFFiNE à un serveur Postgres externe, installez-y pgvector et créez l’extension dans la base de données cible avant d’exécuter la migration.