Self-Hosting AppFlowy Cloud on YunoHost (Docker Compose)

Overview
This guide documents the process of deploying AppFlowy Cloud on a VPS running YunoHost. While AppFlowy provides standard Docker instructions, deploying it alongside an existing ecosystem like YunoHost requires specific workarounds for port conflicts, SSL termination, and internal network routing (Hairpin NAT).
Prerequisites
- Server: VPS running YunoHost (Debian 12 based).
- Domain: A dedicated subdomain (e.g.,
appflowy.example.org). - Tools: Docker & Docker Compose installed.
- Email: A Gmail account (or other SMTP service) for user verification.
1. Directory & Environment Setup
Clone the repository and prepare the environment file.
git clone https://github.com/AppFlowy-IO/AppFlowy-Cloud.git
cd AppFlowy-Cloud
cp deploy.env .env
nano .env
Key .env Configuration
The default configuration must be modified to avoid conflicts with YunoHost's Nginx (ports 80/443).
Critical Changes:
- Ports: Change external ports to avoid YunoHost conflicts.
NGINX_PORT=8085NGINX_TLS_PORT=8443
- URLs: Ensure all frontend URLs point to the public domain, not internal Docker containers. This fixes "Unable to connect" errors on mobile clients.
ADMIN_FRONTEND_GOTRUE_URL=https://appflowy.example.org/gotrue
- Security:
AI_OPENAI_API_KEY=(Leave blank to disable AI and avoid costs).GOTRUE_DISABLE_SIGNUP=true(Enable only after creating your admin account).
2. Docker Compose Customization (The "Hairpin NAT" Fix)
Problem: The Admin Console and Backend services inside Docker often fail to communicate because they cannot resolve the public domain (appflowy.example.org) from inside the container due to firewall restrictions (Loopback/Hairpin NAT issues).
Solution: Use extra_hosts to force internal routing.
Edit docker-compose.yml and add this block to the admin_frontend service:
admin_frontend:
# ... existing config ...
extra_hosts:
- "appflowy.example.org:host-gateway"
Note: This maps the domain directly to the host's internal gateway IP, bypassing external DNS resolution.
3. Nginx Reverse Proxy (YunoHost)
Since AppFlowy is running on port 8085, we need YunoHost to proxy traffic to it.
- Install App: Install the "Redirect" app in YunoHost.
- Configure: Set it to proxy to
http://127.0.0.1:8085. - Optimize: Edit the Nginx configuration file for the Redirect app to support real-time sync and large file uploads.
Add/Uncomment these lines in the Nginx config:
location / {
# ... standard proxy settings ...
# WebSocket Support (Required for Real-Time Sync)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Infinite Uploads & No Buffering
client_max_body_size 0;
proxy_request_buffering off;
proxy_buffering off;
}
4. Solving the "Race Condition" (First Run)
The Issue: On a fresh install, the Authentication Service (gotrue) often starts faster than the Database Migrations.
- Result: The Admin User is created in Auth, but the "Create Profile" trigger hasn't appeared in the database yet. You can log in, but you get a
Record not found: Can't find the user profileerror.
The Fix:
- Wipe and Restart: If the install is fresh, run
docker compose down -vand try again. - Manual Fix (If wipe fails):
- Delete the broken user from the database:
docker compose exec postgres psql -U postgres -d postgres -c "DELETE FROM auth.users WHERE email = 'your@email.com';"
- Wait 60 seconds for the server to stabilize.
- Manually Sign Up via the AppFlowy client (instead of relying on the auto-created admin).
5. Post-Installation Tasks
Promoting to Super Admin
If you had to manually sign up (bypassing the auto-script), you won't be a Super Admin. Promote yourself manually:
docker compose exec postgres psql -U postgres -d postgres -c "UPDATE auth.users SET is_super_admin = true, role = 'service_role' WHERE email = 'your@email.com';"
Mobile App Cache Issues
If you wipe/reinstall the server, the mobile app may cache the old User ID, leading to avatar upload errors (invalid length: expected 32).
- Fix: Go to Android App Info -> Storage -> Clear Storage (not just cache) to force a fresh login.
Summary of "Gotchas"
| Issue | Symptom | Solution |
|---|---|---|
| Port Conflict | Docker container restarts/fails. | Change NGINX_PORT in .env to 8085. |
| Race Condition | "User profile not found" on login. | Delete user via SQL and manually sign up. |
| Hairpin NAT | "Unable to connect" on Admin Login. | Add extra_hosts to docker-compose.yml. |
| Mobile AI Button | Annoying button despite AI disabled. | UI bug in mobile app. Ignore or wait for update. |
No comments to display
No comments to display