自托管笔记应用AppFlowy安装与部署
本文最后更新于 160 天前,其中的信息可能已经有所发展或是发生改变。

一.更新日志

因为AppFlowy-Cloud还在频繁的迭代,文章的教程也会跟着随之变动。大家可看记录的日期来参考部署。这里只列出部署层面相关的改动。

20240501-0.3.32
1.部署的.env配置文件可以指定minio、postgres、redis的地址了。部署更加友好可以复用已有的服务。
2.新增了appflowy_ai的服务 具体作用未知

二.了解介绍

首先介绍一下appflowy,这款软件是我在23年10月份的时候想找oneNote平替软件注意到的,我的主要需求是自托管,那会看官网介绍还没自托管的部署,在12月底的时候想看下appflowy的安卓端是否出来了(咕咕咕);安卓端没找到,倒是看到了自托管的Appflowy Cloud出来了,当即决定部署,经过两天摸索,将安装的步骤整理如下。


自托管的Appflowy Cloud采用Docker compose进行部署,一共有12个容器服务,其中5个服务是可选部署;12个服务如下:

  • nginx:代理服务,用于代理各个服务的地址
  • minio:一个开源的对象存储服务器,它兼容Amazon S3服务接口。用于数据存储。
  • postgres:数据库服务
  • redis:缓存中间件
  • gotrue:身份验证服务器
  • appflowy_cloud:appflowy的核心业务服务
  • appflowy_ai:AI服务
  • admin_frontend(可选):管理登陆授权的web页面服务,官方建议是安装,便于新增用户和登陆。因为不使用第三方的oauth的时候,只能通过web页面登陆然后跳转到appflowy。没看明白的话建议先安装,后续使用就明白了。
  • tunnel(可选):提供将 appflowy 连接到 Cloudflare 的安全方式,无需公共可路由 IP 地址。
  • portainer、portainer_init(可选):两个服务是便于Docker管理的WEB UI。
  • pgadmin(可选):管理postgres 数据库的WEB UI。
    部署的服务架构图如下:

    📌
    可以看到有很多的服务非Appflowy本身的,可以与其他的共用;但是因为docker-compose的配置内容都是默认的,如果使用自己部署的minio、postgres、redis的话注意需要修改docker-compose.yml文件里面的地址并注释掉对应的服务,其中postgres还需要手工打脚本。经过折腾后还是强烈建议不要使用自己部署的minio、postgres、redis,可以省了很多麻烦。

三.部署准备

本着最小化原则,本次部署的教程去除了tunnel、portainer、portainer_init、pgadmin的部署。如果都需部署可以跳过我修改的配置信息。

  • 端口 80/443 可用,这点很重要!!!因为哪怕服务改了端口,但是Appflowy客户端在websocket访问的时候默认的80端口会导致数据无法同步,🥴疯狂踩坑。
  • 运行内存最低2GB,建议4GB
  • 本次教程部署的服务器系统为Debian 11

四.程序部署

1.部署Docker

官网教程地址:https://docs.docker.com/engine/install/debian/
a.运行一下命令卸载相关可能导致冲突的包

for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done

b.设置Docker apt存储库

# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

c.安装Docker

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

d.通过运行hello-world镜像验证运行是否成功

sudo docker run hello-world

2.部署AppFlowy-Cloud

a.登陆服务器拉取最新代码

mkdir appflowy && cd appflowy
git clone https://github.com/AppFlowy-IO/AppFlowy-Cloud
cd AppFlowy-Cloud

b.修改.env配置文件
首先复制一份模板命名为.env

cp deploy.env .env

修改.env的配置信息

## 连接到gotrue docker容器的URL 一般默认即可
APPFLOWY_GOTRUE_BASE_URL=http://gotrue:9999
## 连接到postgres docker容器的URL 如果单独部署数据库的需要更改配置
APPFLOWY_DATABASE_URL=postgres://postgres:password@postgres:5432/postgres
APPFLOWY_ACCESS_CONTROL=true

# admin frontend
## 连接到redis docker容器的URL 如果使用单独部署的redis需要更改配置
ADMIN_FRONTEND_REDIS_URL=redis://redis:6379
## 连接到gotrue docker容器的URL 一般默认即可
ADMIN_FRONTEND_GOTRUE_URL=http://gotrue:9999

# 设置一个加密的密码 任意都可
GOTRUE_JWT_SECRET=benzhu666
# JWT令牌的过期时间(秒)默认即可
GOTRUE_JWT_EXP=7200

#如果设置为true,将自动确认用户注册。
#如果设置了OAuth2或配置了smtp,则可以将其设置为false
#以强制执行电子邮件确认或OAuth2登录。
#如果设置为false,则需要设置SMTP
GOTRUE_MAILER_AUTOCONFIRM=false
# 每分钟可以发送的电子邮件数 默认即可
GOTRUE_RATE_LIMIT_EMAIL_SENT=100

# 设置邮箱的stmp
GOTRUE_SMTP_HOST=smtp.exmail.qq.com
GOTRUE_SMTP_PORT=465
GOTRUE_SMTP_USER=
GOTRUE_SMTP_PASS=
GOTRUE_SMTP_ADMIN_EMAIL=

# gotrue管理员账号 
GOTRUE_ADMIN_EMAIL=my@benzhu.xyz
GOTRUE_ADMIN_PASSWORD=6666666

# 应用的地址
API_EXTERNAL_URL=http://127.0.0.1

# 设置oathu2登录  建议可以设置一个github的 也很方便 参考链接如下:
# https://github.com/AppFlowy-IO/AppFlowy-Cloud/blob/main/doc/AUTHENTICATION.md#github
# Google OAuth2
GOTRUE_EXTERNAL_GOOGLE_ENABLED=false
GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID=
GOTRUE_EXTERNAL_GOOGLE_SECRET=
GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI=http://your-host/gotrue/callback
# GitHub OAuth2
GOTRUE_EXTERNAL_GITHUB_ENABLED=true
GOTRUE_EXTERNAL_GITHUB_CLIENT_ID=
GOTRUE_EXTERNAL_GITHUB_SECRET=
GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI=http://127.0.0.1/gotrue/callback
# Discord OAuth2
GOTRUE_EXTERNAL_DISCORD_ENABLED=false
GOTRUE_EXTERNAL_DISCORD_CLIENT_ID=
GOTRUE_EXTERNAL_DISCORD_SECRET=
GOTRUE_EXTERNAL_DISCORD_REDIRECT_URI=http://your-host/gotrue/callback

# File Storage
# 这是存储图像、文件等存储的地方
# 默认情况下,Minio用作默认文件存储,该存储使用主机的文件系统
# 单独部署的Minio的话需要修改配置 不是的话默认即可
APPFLOWY_S3_USE_MINIO=true
APPFLOWY_S3_MINIO_URL=http://minio:9000 # 如果你使用不同的Minio地址,请更改此项
APPFLOWY_S3_ACCESS_KEY=minioadmin
APPFLOWY_S3_SECRET_KEY=minioadmin
APPFLOWY_S3_BUCKET=appflowy
#APPFLOWY_S3_REGION=us-east-1

# 作用不详 我和上面smtp的配置保持一致
APPFLOWY_MAILER_SMTP_HOST=smtp.exmail.qq.com
APPFLOWY_MAILER_SMTP_USERNAME=
APPFLOWY_MAILER_SMTP_PASSWORD=

RUST_LOG=info

# AppFlowy AI服务
# 这里看了下代码应该是封装openapi的请求服务 api_key填的应该是openai的key
# 实际作用不明 待研究
APPFLOWY_AI_OPENAI_API_KEY=
APPFLOWY_AI_SERVER_HOST=appflowy_ai
APPFLOWY_AI_SERVER_PORT=5001

# AppFlowy History服务
# 理解是历史数据备份用的 但是当前的版本不会创建这个服务 后期迭代应该会加上
APPFLOWY_HISTORY_URL=http://history:50051
APPFLOWY_HISTORY_REDIS_URL=redis://redis:6379
APPFLOWY_HISTORY_DATABASE_URL=postgres://postgres:password@postgres:5432/postgres

# 下面这些服务我都没有部署 直接注释掉
# PgAdmin
#PGADMIN_DEFAULT_EMAIL=admin@example.com
#PGADMIN_DEFAULT_PASSWORD=password

# Portainer (username: admin)
#PORTAINER_PASSWORD=password1234

# Grafana Dashboard
#GF_SECURITY_ADMIN_USER=admin
#GF_SECURITY_ADMIN_PASSWORD=password

# Cloudflare tunnel token
#CLOUDFLARE_TUNNEL_TOKEN=

c.修改docker-compose.yml
新版本的扩展服务单独到了docker-compose-extras.yml,所以docker-compose.yml文件夹无需修改。
但是如果使用了自己单独部署的minio、postgres、redis的话记得还是需要修改docker-compose.yml注释掉对应服务的创建。

d.修改nginx配置
主要是注释可选的服务的代理配置,路径nginx/nginx.conf。
注意果使用了自己单独部署的minio、postgres、redis的话,记得修改对应的代理地址。

# Minimal nginx configuration for AppFlowy-Cloud
# Self Hosted AppFlowy Cloud user should alter this file to suit their needs

events {
    worker_connections 1024;
}

http {
    # docker dns resolver
    resolver 127.0.0.11 valid=10s;

    map $http_upgrade $connection_upgrade {
       default upgrade;
       ''      close;
    }

    server {
        listen 8080;

        # https://github.com/nginxinc/nginx-prometheus-exporter
        location = /stub_status {
            stub_status;
        }
    }

    server {
        ssl_certificate /etc/nginx/ssl/certificate.crt;
        ssl_certificate_key /etc/nginx/ssl/private_key.key;

        listen 80;
        listen 443 ssl;
        client_max_body_size 10M;

        underscores_in_headers on;

        # GoTrue
        location /gotrue/ {
            set $gotrue gotrue;
            proxy_pass http://$gotrue:9999;

            rewrite ^/gotrue(/.*)$ $1 break;

            # Allow headers like redirect_to to be handed over to the gotrue
            # for correct redirecting
            proxy_set_header Host $http_host;
            proxy_pass_request_headers on;
        }

        # WebSocket
        location /ws {
            set $appflowy_cloud appflowy_cloud;
            proxy_pass http://$appflowy_cloud:8000;

            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
            proxy_read_timeout 86400;
        }

        # AppFlowy-Cloud
        # created a separate location block for handling CORS preflight (OPTIONS) requests specifically for the /api endpoint.
        location = /api/options {
            if ($http_origin ~* (http://127.0.0.1:8000)) {
                add_header 'Access-Control-Allow-Origin' $http_origin;
            }
            add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE, PATCH';
            add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version';
            add_header 'Access-Control-Max-Age' 3600;
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            return 204;
        }

        location /api {
            set $appflowy_cloud appflowy_cloud;
            proxy_pass http://$appflowy_cloud:8000;

            proxy_set_header X-Request-Id $request_id;
            proxy_set_header Host $http_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;

            # Set CORS headers for other requests
            if ($http_origin ~* (http://127.0.0.1:8000)) {
                add_header 'Access-Control-Allow-Origin' $http_origin always;
            }
            add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH' always;
            add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version' always;
            add_header 'Access-Control-Max-Age' 3600 always;
        }

        # AppFlowy AI
        location /appflowy_ai/ {
            proxy_pass http://appflowy_ai:5001;
            proxy_set_header Host $http_host;
            proxy_pass_request_headers on;
        }

        # Minio Web UI
        # Derive from: https://min.io/docs/minio/linux/integrations/setup-nginx-proxy-with-minio.html
        # Optional Module, comment this section if you are did not deploy minio in docker-compose.yml
        location /minio/ {
            set $minio minio;
            proxy_pass http://$minio:9001;

            rewrite ^/minio/(.*) /$1 break;
            proxy_set_header Host $http_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;
            proxy_set_header X-NginX-Proxy true;

            ## This is necessary to pass the correct IP to be hashed
            real_ip_header X-Real-IP;

            proxy_connect_timeout 300;

            ## To support websockets in MinIO versions released after January 2023
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            # Some environments may encounter CORS errors (Kubernetes + Nginx Ingress)
            # Uncomment the following line to set the Origin request to an empty string
            # proxy_set_header Origin '';

            chunked_transfer_encoding off;
        }

        # PgAdmin 可选的服务 注释掉
        # Optional Module, comment this section if you are did not deploy pgadmin in docker-compose.yml
        #location /pgadmin/ {
        #    set $pgadmin pgadmin;
        #    proxy_pass http://$pgadmin:80;
        #    proxy_set_header X-Script-Name /pgadmin;
        #    proxy_set_header X-Scheme $scheme;
        #    proxy_set_header Host $host;
        #    proxy_redirect off;
        #}

        # Portainer 可选的服务 注释掉
        # Optional Module, comment this section if you are did not deploy portainer in docker-compose.yml
        #location /portainer/ {
        #    set $portainer portainer;
        #    proxy_pass http://$portainer:9000;
        #rewrite ^/portainer/(.*) /$1 break;
        #}

        # Admin Frontend
        # Optional Module, comment this section if you are did not deploy admin_frontend in docker-compose.yml
        location / {
            set $admin_frontend admin_frontend;
            proxy_pass http://$admin_frontend:3000;

            proxy_set_header X-Scheme $scheme;
            proxy_set_header Host $host;
        }
    }

}

e.启动服务

docker compose up -d

f.检查服务是否正常

docker ps -a

g.重新配置和重新部署
重新配置和重新启动是很常见的。只需编辑并再次.env执行docker compose up -d

五.客户端安装

1.客户端下载

直接下载appflowy的最新版本即可。
https://github.com/AppFlowy-IO/AppFlowy/tags

2.客户端设置

a.安装点击快速进入,然后进入设置


b.在cloud settings里面开启同步后点击重启,重启后进入填入appflowy cloud的地址,默认的是80端口,无需带后缀,比如http://127.0.0.1

c.登录账号
因为目前应用端是没法直接输入账号密码登录的,要么通过othau2登录,要么通过web管理台登录。
othau2没啥好说,直接唤起登录即可,通过管理台登录的话先打开地址:http://127.0.0.1/web/login,将127.0.0.1修改为自己服务的地址,用配置的账号密码进行登录,然后单击open appflowy:

六.相关问题

1.如何自编译admin_frontend

如果我们采用的自己单独部署的redis和postgrep数据库之类的话需要修改,这里提供一下参考。
下载了appflowy cloud源码后进入admin_frontend目录。将内容修改完毕后执行命令编译自己的镜像:

docker compose up -d --no-deps --build admin_frontend

评论

  1. NPC
    2月前
    2024-8-19 17:26:15

    赞,哥们更新下内容?部署运行都没问题,就是经常同步报错囧,但是如果两部pc同时操作一个页面,则可以同步囧
    code: RecordNotFound
    msg: Record not found:Can’t find the row for query: QueryCollab { object_id: “a792dc99-75af-4ba3-8e23-40bd0abfa9cb”, collab_type: Document }

  2. edgar523
    4月前
    2024-6-09 1:13:25

    我2核 2G的机子,勉强能带动。我也是卸下来了。等什么时候appflowy修复能自定义端口吧ヾ(≧∇≦*)ゝ

  3. 匿名
    5月前
    2024-5-22 8:58:55

    这个自托管部署真麻烦,默认占用80端口
    目前有没有魔改版本的

    • xiaozhu
      博主
      匿名
      5月前
      2024-5-22 23:03:40

      目前看没有 我觉得可以等一手官方支持 现在更新很频繁

      • 果子狸
        xiaozhu
        5月前
        2024-5-23 21:59:51

        脑洞大开试试,不知道有没有用zerotier的思路能实现的,让zerotier给appflowy-cloud分配一个ip,每次都打开zerotier-one就能自动同步了。

        • xiaozhu
          博主
          果子狸
          5月前
          2024-5-23 23:58:35

          哈哈 老哥脑洞我没太理解 zerotier是组网工具 和应用没啥关系呀 能部署appflowy-cloud当然也能部署zerotier、WireGuard这类组网vpn进行数据同步呀

  4. Eric
    5月前
    2024-5-19 22:57:25

    请问是否可以修改默认的80和443接口呢?因为我服务器是多个容器公用的,不知道哪个用了443,尝试在nginx.conf中修改了默认的443但是并未生效,不知道为啥

    • xiaozhu
      博主
      Eric
      5月前
      2024-5-20 0:28:09

      改端口服务启动是ok的,nginx.conf的配置不需要改,把docker-compose.yml的nginx服务的端口映射进行更换。因为之前客户端的websocket的请求默认写死了80(不知道新版是否有变动),所以必须要80端口,没有443应该可行。可以尝试下。

      • Eric
        xiaozhu
        5月前
        2024-5-20 0:41:33

        感谢博主深夜回复,我继续尝试下|´・ω・)ノ

        • lulu
          Eric
          1周前
          2024-10-01 15:25:15

          兄弟你成功了吗。。我发现怎么弄都没有办法修改端口都是nginx说80和443端口被占用。

  5. Kim
    6月前
    2024-4-06 10:41:40

    写的非常好,可惜我是小白,只会有群晖这类简单的部署,这个对我来说太复杂了,谢谢。

    • xiaozhu
      博主
      Kim
      6月前
      2024-4-06 11:44:42

      😊不太建议安装,目前部署方式不太友好,使用起来也挺多bug;版本还在不断迭代,可以关注后续变化稳定后再安装。

      • Kim
        xiaozhu
        6月前
        2024-4-06 12:21:11

        谢谢提醒,那我暂时用Joplin过渡下,随时关注。

        • Eric
          Kim
          5月前
          2024-5-20 0:44:22

          我就是先用群晖测试的,这个服务端基于ubuntu,安装过程会大量报错,而且对cpu资源占用奇高,如果是白群不建议用,黑群自己配一台高性能的话倒是可以试试

  6. 毒萝
    6月前
    2024-4-04 15:11:15

    我想通过volume 把docker里面的数据挂载到外边,但是不知道数据的存储路径在哪里。怎么实现

    • xiaozhu
      博主
      毒萝
      6月前
      2024-4-04 15:34:55

      数据是存储在minio里面,可以通过指定minio镜像挂载的路径来实现。

      • 咨询
        xiaozhu
        6月前
        2024-4-06 9:47:47

        redis跟postgre用来做什么的

        • xiaozhu
          博主
          咨询
          6月前
          2024-4-06 11:41:29

          官方文档我没有找到有关postgre和redis有关详细说明;postgre是个数据库,正常来说会存储用户的信息,minio里面的数据通过postgre与用户关联起来等等;redis一般充当缓存中间件和用于多服务数据共享的。

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇