mirror of
https://github.com/streetwriters/notesnook-sync-server.git
synced 2026-08-13 03:50:19 +02:00
docker: fix compose self-hosting and add external Mongo/MinIO/Garage examples (#106)
* docker: update .env and docker-compose.yml for SMTP and new services * docker: add external MinIO, MongoDB, and GarageHQ examples with setup instructions * docs: update README with dotenv docker setup instructions and configuration redirect for external services * docker: update service dependency condition for setup-themes in docker-compose.yml * chore: update example URLs in .env for public services
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# Merge these into the repository root .env (see /.env for all other variables).
|
||||
# Fill after running examples/garage/setup-garage.sh
|
||||
|
||||
GARAGE_ACCESS_KEY_ID=
|
||||
GARAGE_ACCESS_KEY_SECRET=
|
||||
ATTACHMENTS_SERVER_PUBLIC_URL=http://localhost:3900
|
||||
@@ -0,0 +1,57 @@
|
||||
# GarageHQ S3
|
||||
|
||||
[Garage](https://garagehq.deuxfleurs.fr/) replaces embedded MinIO. The Notesnook stack uses the root [`docker-compose.yml`](../../docker-compose.yml) with [`notesnook.override.yml`](notesnook.override.yml). MongoDB stays embedded.
|
||||
|
||||
Based on [PR #79](https://github.com/streetwriters/notesnook-sync-server/pull/79), with community review adjustments:
|
||||
|
||||
- Notesnook uses **presigned URLs** — no public bucket policy required.
|
||||
- Do **not** use `aws s3api put-bucket-policy` for public read ([Garage S3 compatibility](https://garagehq.deuxfleurs.fr/documentation/reference-manual/s3-compatibility/)).
|
||||
- Use port **3900** (S3 API) for `ATTACHMENTS_SERVER_PUBLIC_URL` when not behind a reverse proxy.
|
||||
|
||||
## Setup
|
||||
|
||||
From the **repository root**:
|
||||
|
||||
```bash
|
||||
# 1. Generate RPC secret and add to root .env or use --env-file
|
||||
openssl rand -hex 32
|
||||
# add GARAGE_RPC_SECRET=... to .env (see infra.env.example)
|
||||
|
||||
# 2. Garage infra
|
||||
docker compose -f examples/garage/infra.compose.yml up -d
|
||||
|
||||
# 3. One-time bucket/key setup (from repo root)
|
||||
./examples/garage/setup-garage.sh
|
||||
|
||||
# 4. Merge keys into root .env (see .env.example)
|
||||
# 5. Notesnook
|
||||
docker compose -f docker-compose.yml -f examples/garage/notesnook.override.yml up -d
|
||||
```
|
||||
|
||||
`notesnook-server` may stay unhealthy until valid `GARAGE_ACCESS_KEY_*` values are in root `.env`:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yml -f examples/garage/notesnook.override.yml up -d --force-recreate notesnook-server
|
||||
```
|
||||
|
||||
## Environment
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| [`infra.env.example`](infra.env.example) | `GARAGE_RPC_SECRET` for infra |
|
||||
| [`.env.example`](.env.example) | `GARAGE_ACCESS_KEY_*` and `ATTACHMENTS_SERVER_PUBLIC_URL` (merge into root `.env`) |
|
||||
|
||||
## Stop
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yml -f examples/garage/notesnook.override.yml down
|
||||
docker compose -f examples/garage/infra.compose.yml down
|
||||
```
|
||||
|
||||
## Manual setup
|
||||
|
||||
If `setup-garage.sh` fails, follow PR #79 layout/bucket/key steps manually. Skip public bucket policy. Optional bucket alias only for Garage web port 3902.
|
||||
|
||||
## MongoDB
|
||||
|
||||
This example only externalizes S3. For external MongoDB, combine with [external-mongodb](../external-mongodb/) or use the root all-in-one compose.
|
||||
@@ -0,0 +1,21 @@
|
||||
metadata_dir = "/var/lib/garage/meta"
|
||||
data_dir = "/var/lib/garage/data"
|
||||
db_engine = "lmdb"
|
||||
|
||||
replication_factor = 1
|
||||
consistency_mode = "consistent"
|
||||
|
||||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_public_addr = "127.0.0.1:3901"
|
||||
|
||||
[s3_api]
|
||||
s3_region = "us-east-1"
|
||||
api_bind_addr = "[::]:3900"
|
||||
root_domain = ".s3.garage"
|
||||
|
||||
[s3_web]
|
||||
bind_addr = "[::]:3902"
|
||||
root_domain = ".web.garage"
|
||||
|
||||
[admin]
|
||||
api_bind_addr = "[::]:3903"
|
||||
@@ -0,0 +1,31 @@
|
||||
name: notesnook-garage-infra
|
||||
|
||||
services:
|
||||
notesnook-garage:
|
||||
image: dxflrs/garage:v2.3.0
|
||||
hostname: notesnook-garage
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3900:3900
|
||||
- 3901:3901
|
||||
- 3902:3902
|
||||
networks:
|
||||
- notesnook-shared
|
||||
volumes:
|
||||
- s3data:/var/lib/garage
|
||||
- ./garage.toml:/etc/garage.toml:ro
|
||||
environment:
|
||||
GARAGE_RPC_SECRET: ${GARAGE_RPC_SECRET}
|
||||
healthcheck:
|
||||
test: ["CMD", "/garage", "status"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
networks:
|
||||
notesnook-shared:
|
||||
name: notesnook-shared
|
||||
|
||||
volumes:
|
||||
s3data:
|
||||
@@ -0,0 +1,2 @@
|
||||
# Generate with: openssl rand -hex 32
|
||||
GARAGE_RPC_SECRET=
|
||||
@@ -0,0 +1,22 @@
|
||||
services:
|
||||
notesnook-s3:
|
||||
profiles: [embedded-disabled]
|
||||
|
||||
setup-s3:
|
||||
profiles: [embedded-disabled]
|
||||
|
||||
notesnook-server:
|
||||
networks:
|
||||
- notesnook
|
||||
- notesnook-shared
|
||||
depends_on: !override
|
||||
identity-server:
|
||||
condition: service_started
|
||||
environment:
|
||||
S3_INTERNAL_SERVICE_URL: "http://notesnook-garage:3900"
|
||||
S3_ACCESS_KEY_ID: "${GARAGE_ACCESS_KEY_ID}"
|
||||
S3_ACCESS_KEY: "${GARAGE_ACCESS_KEY_SECRET}"
|
||||
|
||||
networks:
|
||||
notesnook-shared:
|
||||
external: true
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
NC='\033[0m'
|
||||
|
||||
BUCKET="${GARAGE_BUCKET:-attachments}"
|
||||
ZONE="${GARAGE_ZONE:-us-east-1}"
|
||||
CAPACITY="${GARAGE_CAPACITY:-10G}"
|
||||
KEY_NAME="${GARAGE_KEY_NAME:-notesnook-key}"
|
||||
ENDPOINT="${GARAGE_S3_ENDPOINT:-http://127.0.0.1:3900}"
|
||||
|
||||
echo -e "${GREEN}--- Notesnook Garage setup ---${NC}"
|
||||
|
||||
CONTAINER_NAME="${GARAGE_CONTAINER_NAME:-}"
|
||||
if [ -z "$CONTAINER_NAME" ]; then
|
||||
CONTAINER_NAME=$(docker ps --format '{{.Names}}' | grep -E 'notesnook-garage|garage' | head -n 1 || true)
|
||||
fi
|
||||
|
||||
if [ -z "$CONTAINER_NAME" ]; then
|
||||
echo -e "${RED}Error: no running Garage container found.${NC}"
|
||||
echo "Start infra first (from repo root): docker compose -f examples/garage/infra.compose.yml up -d"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "Using container: ${GREEN}${CONTAINER_NAME}${NC}"
|
||||
|
||||
echo -e "\n${YELLOW}[1/5] Layout${NC}"
|
||||
NODE_ID=""
|
||||
for _ in $(seq 1 10); do
|
||||
NODE_ID=$(docker exec "$CONTAINER_NAME" /garage node id -q | cut -d '@' -f 1 || true)
|
||||
[ -n "$NODE_ID" ] && break
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ -z "$NODE_ID" ]; then
|
||||
echo -e "${RED}Error: could not read Garage node id.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Node ID: $NODE_ID"
|
||||
docker exec "$CONTAINER_NAME" /garage layout assign -z "$ZONE" -c "$CAPACITY" "$NODE_ID"
|
||||
echo "yes" | docker exec -i "$CONTAINER_NAME" /garage layout apply --version 1
|
||||
|
||||
echo -e "\n${YELLOW}[2/5] Bucket${NC}"
|
||||
docker exec "$CONTAINER_NAME" /garage bucket create "$BUCKET" || true
|
||||
|
||||
if [ -n "${GARAGE_BUCKET_ALIAS:-}" ]; then
|
||||
echo "Aliasing bucket to ${GARAGE_BUCKET_ALIAS} (optional website access)"
|
||||
docker exec "$CONTAINER_NAME" /garage bucket alias "$BUCKET" "$GARAGE_BUCKET_ALIAS"
|
||||
fi
|
||||
|
||||
echo -e "\n${YELLOW}[3/5] API key${NC}"
|
||||
KEY_INFO=$(docker exec "$CONTAINER_NAME" /garage key create "$KEY_NAME")
|
||||
echo "$KEY_INFO"
|
||||
|
||||
KEY_ID=$(echo "$KEY_INFO" | awk '/Key ID:/ {print $3}')
|
||||
SECRET_KEY=$(echo "$KEY_INFO" | awk '/Secret key:/ {print $3}')
|
||||
|
||||
if [ -z "$KEY_ID" ] || [ -z "$SECRET_KEY" ]; then
|
||||
echo -e "${RED}Error: failed to parse key id/secret from garage output.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "\n${YELLOW}[4/5] Bucket permissions${NC}"
|
||||
docker exec "$CONTAINER_NAME" /garage bucket allow "$BUCKET" --read --write --owner --key "$KEY_ID"
|
||||
|
||||
echo -e "\n${YELLOW}[5/5] CORS (aws-cli)${NC}"
|
||||
docker run --rm \
|
||||
--env AWS_ACCESS_KEY_ID="$KEY_ID" \
|
||||
--env AWS_SECRET_ACCESS_KEY="$SECRET_KEY" \
|
||||
--add-host=host.docker.internal:host-gateway \
|
||||
amazon/aws-cli \
|
||||
--endpoint-url "$ENDPOINT" \
|
||||
s3api put-bucket-cors --bucket "$BUCKET" --cors-configuration '{
|
||||
"CORSRules": [
|
||||
{
|
||||
"AllowedHeaders": ["*"],
|
||||
"AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
|
||||
"AllowedOrigins": ["*"],
|
||||
"ExposeHeaders": ["ETag"]
|
||||
}
|
||||
]
|
||||
}'
|
||||
|
||||
cat <<EOF
|
||||
|
||||
${GREEN}--- Setup complete ---${NC}
|
||||
|
||||
Merge these into the repository root .env (see examples/garage/.env.example):
|
||||
|
||||
GARAGE_ACCESS_KEY_ID=$KEY_ID
|
||||
GARAGE_ACCESS_KEY_SECRET=$SECRET_KEY
|
||||
ATTACHMENTS_SERVER_PUBLIC_URL=$ENDPOINT
|
||||
|
||||
Notes:
|
||||
- Notesnook uses presigned URLs for attachments; public bucket policies are not required.
|
||||
- Use the S3 API URL (port 3900) for ATTACHMENTS_SERVER_PUBLIC_URL unless you proxy it.
|
||||
- Do not use aws s3api put-bucket-policy for public read; Garage does not support this like MinIO.
|
||||
|
||||
Then restart the sync server (from repo root):
|
||||
|
||||
docker compose -f docker-compose.yml -f examples/garage/notesnook.override.yml up -d --force-recreate notesnook-server
|
||||
|
||||
EOF
|
||||
Reference in New Issue
Block a user