Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tutorial for setting up AppFlowy on Hetzner Cloud #959

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
---
SPDX-License-Identifier: MIT
path: "/tutorials/install-and-configure-appflowy-with-hetzner-object-storage"
slug: "install-and-configure-appflowy-with-hetzner-object-storage"
date: "2024-10-01"
title: "Install and configure AppFlowy with Hetzner Object Storage"
short_description: "A guide on how to setup your own AppFlowy Cloud Server using Hetzner Object Storage and Hetzner Cloud Server."
tags: ["AppFlowy", "Object Storage", "Cloud"]
author: "Stefan Weiberg"
author_link: "https://github.com/suntorytimed"
author_img: "https://avatars.githubusercontent.com/u/2744377"
author_description: ""
language: "en"
available_languages: ["en"]
header_img: "header-4"
cta: "objectstorage"
---

## Introduction

In this tutorial we are going to setup our own AppFlowy Cloud server using the official documentation by AppFlowy. We will use a Hetzner Cloud Server and the new Hetzner Object Storage. This tutorial won't cover how you create a new project called "AppFlowy", and how Hetzner Cloud servers are created. We will purely focus on setting up AppFlowy, setting up a bucket and connecting it to our AppFlowy Cloud server.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe add a few links here?

Suggested change
In this tutorial we are going to setup our own AppFlowy Cloud server using the official documentation by AppFlowy. We will use a Hetzner Cloud Server and the new Hetzner Object Storage. This tutorial won't cover how you create a new project called "AppFlowy", and how Hetzner Cloud servers are created. We will purely focus on setting up AppFlowy, setting up a bucket and connecting it to our AppFlowy Cloud server.
In this tutorial we are going to setup our own [AppFlowy](https://appflowy.io/) Cloud server using the official documentation by AppFlowy. We will use a [Hetzner Cloud Server](https://hetzner.cloud) and the new [Hetzner Object Storage](https://docs.hetzner.com/storage/object-storage). This tutorial won't cover how you create a new project called "AppFlowy", and how Hetzner Cloud servers are created. We will purely focus on setting up AppFlowy, setting up a bucket and connecting it to our AppFlowy Cloud server.


### Prerequisites

* Hetzner Cloud server with git, docker and docker-compose installed and access via SSH or web terminal in the Hetzner Cloud console
* DNS entry for your desired (sub-)domain to your Hetzner Cloud server
* Optional: Dedicated Hetzner Cloud project called "AppFlowy" to pool you resources together

## Step 1 - Create a bucket

First, we will create a new bucket in the Hetzner Cloud console.

![Click on "Create Bucket" in your project](images/create_bucket.png)

As the name of the bucket we chooose appflowy and add a UUID after it to make it unique. We can create the bucket with private visibility, as the access will happen via the Hetzner Cloud server.

![Choose a unique bucket name and click on "Create & Buy now"](images/name_bucket.png)

After successfully creating the bucket, open it and copy the URL of the bucket, by clicking on it. Paste the URL into a file for later. Click on "Manage credentials" on the same screen to access the page for S3 credential creation.

![Copy the URL of your bucket](images/bucket_url.png)

Click on "Generate credentials" to create new credentials for your AppFlowy Cloud server.

![Click on "Generate credentials"](images/generate_credentials.png)

Choose a description for your new credentials.

![Click on "Generate credentials"](images/description_credentials.png)

Copy and store your access key and secret key for later usage.

![Copy your keys](images/key_credentials.png)

## Step 2 - Setup your AppFlowy Cloud server

SSH into your server or use the Hetzner Cloud Console terminal to access your server via the web frontend.

### Step 2.1 - Clone the AppFlowy Cloud repository

Clone the AppFlowy-Cloud repository to your desired location and `cd` into the cloned repository:

```
git clone https://github.com/AppFlowy-IO/AppFlowy-Cloud
cd AppFlowy-Cloud
```

### Step 2.2 - Setup the nginx proxy of AppFlowy Cloud to use your SSL certificate

Put your SSL certificate and private key in the folder ./nginx/ssl/ or adjust the location in the docker-compose.yml to point to your own certificates. Alternatively you can create a Let's Encrypt SSL certificate via DNS challenge on the Hetzner DNS service by using lego:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Put your SSL certificate and private key in the folder ./nginx/ssl/ or adjust the location in the docker-compose.yml to point to your own certificates. Alternatively you can create a Let's Encrypt SSL certificate via DNS challenge on the Hetzner DNS service by using lego:
Put your SSL certificate and private key in the folder `./nginx/ssl/` or adjust the location in the `docker-compose.yml` to point to your own certificates. Alternatively you can create a Let's Encrypt SSL certificate via DNS challenge on the Hetzner DNS service by using lego:


```
go install github.com/go-acme/lego/v4/cmd/lego@latest
~/go/bin/lego --domains="appflowy.example.com" --email="[email protected]" --http run
```

And adjust the docker-compose.yml to point to the output folder of lego:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
And adjust the docker-compose.yml to point to the output folder of lego:
And adjust the `docker-compose.yml` to point to the output folder of lego:


```
nginx:
restart: on-failure
image: nginx
ports:
- ${NGINX_PORT:-80}:80 # Disable this if you are using TLS
- ${NGINX_TLS_PORT:-443}:443
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./.lego/certificates/appflowy.example.com.crt:/etc/nginx/ssl/certificate.crt
- ./.lego/certificates/appflowy.example.com.key:/etc/nginx/ssl/private_key.key
```

### Step 2.3 - Remove the minio service from the docker-compose.yml

Remove the minio service from the docker-compose.yml by deleting following lines:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Remove the minio service from the docker-compose.yml by deleting following lines:
Remove the minio service from the `docker-compose.yml` by deleting following lines:


```
# You do not need this if you have configured to use your own s3 file storage
minio:
restart: on-failure
image: minio/minio
environment:
- MINIO_BROWSER_REDIRECT_URL=http://localhost/minio
- MINIO_ROOT_USER=${APPFLOWY_S3_ACCESS_KEY:-minioadmin}
- MINIO_ROOT_PASSWORD=${APPFLOWY_S3_SECRET_KEY:-minioadmin}
command: server /data --console-address ":9001"
volumes:
- minio_data:/data
```

Don't forget to remove the minio_data volume from the volumes at the end of the docker-compose.yml. Only the postgres_data volume should remain:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Don't forget to remove the minio_data volume from the volumes at the end of the docker-compose.yml. Only the postgres_data volume should remain:
Don't forget to remove the `minio_data` volume from the volumes at the end of the `docker-compose.yml`. Only the `postgres_data `volume should remain:


```
volumes:
postgres_data:
```

### Step 2.4 - Adjust the deploy.env

Now we need to adjust the deploy.env file to use our own passwords and point to the Hetzner Object Storage. I recommend to keep the order of values identical, as we are going to commit our changes locally. This makes rebasing easier. At the time of creation of this tutorial you should adjust following values with your own password and SMTP configuration:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Now we need to adjust the deploy.env file to use our own passwords and point to the Hetzner Object Storage. I recommend to keep the order of values identical, as we are going to commit our changes locally. This makes rebasing easier. At the time of creation of this tutorial you should adjust following values with your own password and SMTP configuration:
Now we need to adjust the `deploy.env` file to use our own passwords and point to the Hetzner Object Storage. I recommend to keep the order of values identical, as we are going to commit our changes locally. This makes rebasing easier. At the time of creation of this tutorial you should adjust following values with your own password and SMTP configuration:


```
# change password and secret
# change your database name if needed
APPFLOWY_DATABASE_URL=postgres://postgres:password@postgres:5432/postgres
GOTRUE_JWT_SECRET=hello456
GOTRUE_DATABASE_URL=postgres://supabase_auth_admin:root@postgres:5432/postgres
APPFLOWY_AI_DATABASE_URL=postgresql+psycopg://postgres:password@postgres:5432/postgres
APPFLOWY_HISTORY_DATABASE_URL=postgres://postgres:password@postgres:5432/postgres
APPFLOWY_INDEXER_DATABASE_URL=postgres://postgres:password@postgres:5432/postgres

# replace with your own SMTP configuration
GOTRUE_SMTP_HOST=smtp.gmail.com
GOTRUE_SMTP_PORT=465
GOTRUE_SMTP_USER=email_sender@some_company.com
GOTRUE_SMTP_PASS=email_sender_password
GOTRUE_SMTP_ADMIN_EMAIL=comp_admin@some_company.com
APPFLOWY_MAILER_SMTP_HOST=smtp.gmail.com
APPFLOWY_MAILER_SMTP_PORT=465
APPFLOWY_MAILER_SMTP_USERNAME=email_sender@some_company.com
APPFLOWY_MAILER_SMTP_PASSWORD=email_sender_password

# configure your admin account
[email protected]
GOTRUE_ADMIN_PASSWORD=password

# input your (sub-)domain, which you configured in the Hetzner DNS service
API_EXTERNAL_URL=http://your-host

```

Remove following lines from the deploy.env, as we won't use those services:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Remove following lines from the deploy.env, as we won't use those services:
Remove following lines from the `deploy.env`file, as we won't use those services:


```
# PgAdmin
# Optional module to manage the postgres database
# You can access the pgadmin at http://your-host/pgadmin
# Refer to the APPFLOWY_DATABASE_URL for password when connecting to the database
[email protected]
PGADMIN_DEFAULT_PASSWORD=password

# Portainer (username: admin)
PORTAINER_PASSWORD=password1234

# Cloudflare tunnel token
CLOUDFLARE_TUNNEL_TOKEN=
```

And finally configure your Object Storage:

```
APPFLOWY_S3_USE_MINIO=true
APPFLOWY_S3_MINIO_URL=https://appflowy-5d3f1744a2494fb5b49c983e3eff3e17.fsn1.your-objectstorage.com # this is where the previously copied Object Storage URL is pasted
APPFLOWY_S3_ACCESS_KEY=<your access key> # paste your previously generated access key from the S3 credentials
APPFLOWY_S3_SECRET_KEY=<your secret key> # paste your previously generated secret key from the S3 credentials
APPFLOWY_S3_BUCKET=appflowy # this isn't really the name of your bucket, but the root folder in your bucket
#APPFLOWY_S3_REGION=us-east-1
```

### Step 2.5 - Add and commit your changes

Now we use git to store our changes in the git log and make it easier to pull updates from upstream. By using this method every change of defaults in the deploy.env and docker-compose.yml will result in merge conflicts when pulling an update, which you need to resolve before restarting/updating your server.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Now we use git to store our changes in the git log and make it easier to pull updates from upstream. By using this method every change of defaults in the deploy.env and docker-compose.yml will result in merge conflicts when pulling an update, which you need to resolve before restarting/updating your server.
Now we use git to store our changes in the git log and make it easier to pull updates from upstream. By using this method every change of defaults in the `deploy.env` and `docker-compose.yml` will result in merge conflicts when pulling an update, which you need to resolve before restarting/updating your server.


```
git add deploy.env docker-compose.yml
git commit -m "my own AppFlowy deployment on Hetzner Cloud"
```

## Step 3

Start you AppFlowy Cloud server via docker-compose (or docker compose, depending on your installation method):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Start you AppFlowy Cloud server via docker-compose (or docker compose, depending on your installation method):
Start you AppFlowy Cloud server via `docker-compose` (or `docker compose`, depending on your installation method):


```
docker-compose --env-file deploy.env up -d
```

## Step 4 - Configure AppFlowy App to point to your new server

Go into the cloud settings of AppFlowy and change your server to "AppFlowy Cloud Self-hosted". Enable the sync and enter the URL of your cloud server as Base URL. Restart AppFlowy.

![Change login server in AppFlowy](images/appflowy_config.png)

## Step 5 - User creation and login

Open a browser and navigate to the URL of your cloud server. The admin frontend will open up and you can login with the admin credentials you previously defined in the deploy.env. Via the invite page you can now create/invite new users:

![Invite new users](images/user_invite.png)

The new users receive a magic link for login. In the web frontend you also have the option to directly open AppFlowy with the account you logged into the web frontend with.

![Open AppFlowy](images/open_appflowy.png)

## Step 6 - Check if the object storage works

The object storage will be used for storing images and videos you add in AppFlowy. Open up the AppFlowy application and add an image in a random page.

![Add image to a random page in AppFlowy](images/image_appflowy.png)

You can now check in you Hetzner Cloud console if the file got created in the bucket.

![Check if image is uploaded to the object storage](images/image_console.png)

## Conclusion

We have setup our own AppFlowy Cloud server using a Hetzner Cloud server and the Hetzner Cloud Object Storage.

##### License: MIT

<!--

Contributor's Certificate of Origin

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I have
the right to submit it under the license indicated in the file; or

(b) The contribution is based upon previous work that, to the best of my
knowledge, is covered under an appropriate license and I have the
right under that license to submit that work with modifications,
whether created in whole or in part by me, under the same license
(unless I am permitted to submit under a different license), as
indicated in the file; or

(c) The contribution was provided directly to me by some other person
who certified (a), (b) or (c) and I have not modified it.

(d) I understand and agree that this project and the contribution are
public and that a record of the contribution (including all personal
information I submit with it, including my sign-off) is maintained
indefinitely and may be redistributed consistent with this project
or the license(s) involved.

Signed-off-by: Stefan Weiberg

-->
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.