From cd5e8796d03120896af00b83c3d3d02121096c4e Mon Sep 17 00:00:00 2001 From: Bill Yang <45103519+goldflag@users.noreply.github.com> Date: Tue, 6 May 2025 13:08:41 -0700 Subject: [PATCH] Enhance setup and configuration for self-hosting (#125) * Enhance setup and configuration for self-hosting - Updated `.env.example` to include new webserver configuration options. - Modified `docker-compose.yml` to support custom port bindings for backend and client services when not using the built-in webserver. - Enhanced `setup.sh` to handle `--no-webserver` flag, allowing users to run their own webserver and exposing necessary ports. - Expanded documentation in `self-hosting.mdx` to include instructions for using a custom webserver and managing services. * Refactor environment variable configuration and enhance setup script - Updated `.env.example` to use HOST_BACKEND_PORT and HOST_CLIENT_PORT for clearer port mapping. - Modified `docker-compose.yml` to reflect changes in environment variable names for backend and client services. - Enhanced `setup.sh` to support custom port options and improved error handling for missing `.env` file. - Updated documentation in `self-hosting.mdx` to include new setup script options and examples for custom port configurations. * docker publish --- .env.example | 13 +++ .github/workflows/docker-publish.yml | 72 ++++++++++++++++ docker-compose.yml | 4 + docs/src/content/self-hosting.mdx | 120 +++++++++++++++++++++++++-- restart.sh | 30 +++++++ setup.sh | 105 +++++++++++++++++++++-- start.sh | 26 ++++++ stop.sh | 9 ++ update.sh | 38 +++++++++ 9 files changed, 407 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/docker-publish.yml create mode 100644 restart.sh create mode 100644 start.sh create mode 100644 stop.sh create mode 100644 update.sh diff --git a/.env.example b/.env.example index 363cd8a..bc38d70 100644 --- a/.env.example +++ b/.env.example @@ -1,12 +1,25 @@ +# Domain and URL Configuration DOMAIN_NAME=demo.rybbit.io BASE_URL="https://${DOMAIN_NAME}" + +# Authentication and Security BETTER_AUTH_SECRET=insecure-secret DISABLE_SIGNUP=false +# Webserver Configuration +# Set to false to disable the built-in Caddy webserver +USE_WEBSERVER=true +# Host port mappings - these control how services are exposed +# Format: "host_binding:container_port" or "port:container_port" +HOST_BACKEND_PORT=127.0.0.1:3001 +HOST_CLIENT_PORT=127.0.0.1:3002 + +# ClickHouse Database Configuration CLICKHOUSE_DB=analytics CLICKHOUSE_USER=default CLICKHOUSE_PASSWORD=frog +# PostgreSQL Database Configuration POSTGRES_DB=analytics POSTGRES_USER=frog POSTGRES_PASSWORD=frog diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..f777bd7 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,72 @@ +name: Docker Publish + +on: + push: + branches: + - master + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker - Backend + id: meta_backend + uses: docker/metadata-action@v5 + with: + # Images will be ghcr.io/rybbit-io/rybbit-backend + images: ghcr.io/${{ github.repository_owner }}/rybbit-backend + tags: | + type=ref,event=branch # Creates tag like :master + type=sha # Creates tag like : + # Tags 'master' branch pushes additionally with 'latest' + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }} + + - name: Build and push Backend Docker image + uses: docker/build-push-action@v6 + with: + context: ./server # + file: ./server/Dockerfile + push: true + tags: ${{ steps.meta_backend.outputs.tags }} + labels: ${{ steps.meta_backend.outputs.labels }} + # Optional: Enable build cache for potentially faster builds + cache-from: type=gha,scope=${{ github.workflow }}-backend + cache-to: type=gha,scope=${{ github.workflow }}-backend,mode=max + + - name: Extract metadata (tags, labels) for Docker - Client + id: meta_client + uses: docker/metadata-action@v5 + with: + # Images will be ghcr.io/rybbit-io/rybbit-client + images: ghcr.io/${{ github.repository_owner }}/rybbit-client + tags: | + type=ref,event=branch + type=sha + # Tags 'master' branch pushes additionally with 'latest' + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'master') }} + + - name: Build and push Client Docker image + uses: docker/build-push-action@v6 + with: + context: ./client + file: ./client/Dockerfile + push: true + tags: ${{ steps.meta_client.outputs.tags }} + labels: ${{ steps.meta_client.outputs.labels }} + # Optional: Enable build cache for potentially faster builds + cache-from: type=gha,scope=${{ github.workflow }}-client + cache-to: type=gha,scope=${{ github.workflow }}-client,mode=max diff --git a/docker-compose.yml b/docker-compose.yml index 9d1b0a8..a34c912 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -56,6 +56,8 @@ services: build: context: ./server dockerfile: Dockerfile + ports: + - "${HOST_BACKEND_PORT:-127.0.0.1:3001}:3001" environment: - NODE_ENV=production - CLICKHOUSE_HOST=http://clickhouse:8123 @@ -83,6 +85,8 @@ services: dockerfile: Dockerfile args: NEXT_PUBLIC_BACKEND_URL: ${BASE_URL} + ports: + - "${HOST_CLIENT_PORT:-127.0.0.1:3002}:3002" environment: - NODE_ENV=production - NEXT_PUBLIC_BACKEND_URL=${BASE_URL} diff --git a/docs/src/content/self-hosting.mdx b/docs/src/content/self-hosting.mdx index 5436945..9683ded 100644 --- a/docs/src/content/self-hosting.mdx +++ b/docs/src/content/self-hosting.mdx @@ -52,14 +52,97 @@ cd rybbit The repository includes a setup script that configures the necessary environment variables (including generating a secure secret) and starts the application using Docker Compose. -Run the script, replacing `your.domain.name` with the domain or subdomain you configured in the prerequisites: + + Important: Make all scripts executable before proceeding! + ```bash + chmod +x *.sh + ``` + + +Run the setup script, replacing `your.domain.name` with the domain or subdomain you configured in the prerequisites: ```bash -chmod +x setup.sh ./setup.sh your.domain.name ``` The script will create a `.env` file and then build and start the containers. This might take a few minutes the first time. +#### Setup Script Options + +The setup script supports several options: + +```bash +./setup.sh [options] +``` + +Available options: +- `--no-webserver`: Disable the built-in Caddy webserver +- `--backend-port `: Set custom host port for backend (default: 3001) +- `--client-port `: Set custom host port for client (default: 3002) +- `--help`: Show help message + +Examples: +```bash +# Custom ports with built-in webserver +./setup.sh tracking.example.com --backend-port 8080 --client-port 8081 + +# Custom ports with your own webserver +./setup.sh tracking.example.com --no-webserver --backend-port 8080 --client-port 8081 +``` + + + When you specify custom ports, only the host port mapping changes. Inside the Docker containers, the services still use ports 3001 and 3002. + + +#### Using Your Own Web Server + +If you prefer to use your own web server (such as Nginx or Apache) instead of the built-in Caddy server, you can use the `--no-webserver` flag: + +```bash +./setup.sh your.domain.name --no-webserver +``` + +This will: +- Not start the Caddy container +- Expose the backend service on host port 3001 (or your custom port) +- Expose the client service on host port 3002 (or your custom port) + +You'll need to configure your web server to proxy requests to these services. Here's an example Nginx configuration: + +```nginx +server { + listen 80; + server_name your.domain.name; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl; + server_name your.domain.name; + + # SSL configuration (using Let's Encrypt) + ssl_certificate /etc/letsencrypt/live/your.domain.name/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/your.domain.name/privkey.pem; + + # API requests - adjust port if you customized it + location /api/ { + proxy_pass http://localhost:3001; # or your custom port + 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 app - adjust port if you customized it + location / { + proxy_pass http://localhost:3002; # or your custom port + 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; + } +} +``` + ### 5. Sign Up Once the services are running and DNS has propagated, Caddy (the webserver) will automatically obtain an SSL certificate for your domain. @@ -68,11 +151,38 @@ Open your browser and navigate to `https://your.domain.name/signup` (using the d Create your admin account. You can then log in and start adding your websites! - - -Congratulations! You have successfully self-hosted Rybbit. +## Managing Your Installation + +Rybbit comes with several helper scripts to manage your installation: + + + If you haven't done so already, make sure all scripts are executable: + ```bash + chmod +x *.sh + ``` + + +- **start.sh**: Start all services after they've been stopped + ```bash + ./start.sh + ``` + +- **stop.sh**: Stop all running services + ```bash + ./stop.sh + ``` + +- **restart.sh**: Restart all services + ```bash + ./restart.sh + ``` + +- **update.sh**: Update to the latest version by pulling new code and rebuilding containers + ```bash + ./update.sh + ``` If you are using your self-hosted Rybbit without anyone else, you can disable new user signups by setting `DISABLE_SIGNUP=true` in the `.env` file at the root of the repository. diff --git a/restart.sh b/restart.sh new file mode 100644 index 0000000..5df7be3 --- /dev/null +++ b/restart.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status. +set -e + +echo "Restarting services..." + +# Stop all services +docker compose stop + +# Check if .env file exists +if [ ! -f .env ]; then + echo "Error: .env file not found. Please run setup.sh first." + echo "Usage: ./setup.sh " + exit 1 +fi + +# Load environment variables +source .env + +# Start the appropriate services +if [ "$USE_WEBSERVER" = "false" ]; then + # Start without the caddy service when using --no-webserver + docker compose start backend client clickhouse postgres +else + # Start all services including caddy + docker compose start +fi + +echo "Services restarted. You can monitor logs with: docker compose logs -f" \ No newline at end of file diff --git a/setup.sh b/setup.sh index 3a5281a..48b55f1 100644 --- a/setup.sh +++ b/setup.sh @@ -3,14 +3,91 @@ # Exit immediately if a command exits with a non-zero status. set -e -# Check if domain name argument is provided -if [ -z "$1" ]; then - echo "Usage: $0 " +# Default values +USE_WEBSERVER="true" +BACKEND_PORT="3001" +CLIENT_PORT="3002" +HOST_BACKEND_PORT="127.0.0.1:3001" +HOST_CLIENT_PORT="127.0.0.1:3002" + +# Help function +show_help() { + echo "Usage: $0 [options]" echo "Example: $0 myapp.example.com" + echo "Example with no webserver: $0 myapp.example.com --no-webserver" + echo "" + echo "Options:" + echo " --no-webserver Disable the built-in Caddy webserver" + echo " --backend-port Set custom host port for backend (default: 3001)" + echo " --client-port Set custom host port for client (default: 3002)" + echo " --help Show this help message" +} + +# Parse arguments +while [[ "$#" -gt 0 ]]; do + case $1 in + --no-webserver) + USE_WEBSERVER="false" + shift + ;; + --backend-port) + if [[ -z "$2" || "$2" =~ ^- ]]; then + echo "Error: --backend-port requires a port number" + show_help + exit 1 + fi + BACKEND_PORT="$2" + shift 2 + ;; + --client-port) + if [[ -z "$2" || "$2" =~ ^- ]]; then + echo "Error: --client-port requires a port number" + show_help + exit 1 + fi + CLIENT_PORT="$2" + shift 2 + ;; + --help) + show_help + exit 0 + ;; + -*) + echo "Unknown option: $1" + show_help + exit 1 + ;; + *) + if [ -z "$DOMAIN_NAME" ]; then + DOMAIN_NAME="$1" + else + echo "Error: Only one domain name can be specified" + show_help + exit 1 + fi + shift + ;; + esac +done + +# Update port mappings after all args are parsed +if [ "$USE_WEBSERVER" = "false" ]; then + # When not using the built-in webserver, expose ports to host + HOST_BACKEND_PORT="${BACKEND_PORT}:3001" + HOST_CLIENT_PORT="${CLIENT_PORT}:3002" +else + # Keep ports only accessible via localhost when using Caddy + HOST_BACKEND_PORT="127.0.0.1:3001" + HOST_CLIENT_PORT="127.0.0.1:3002" +fi + +# Check if domain name argument is provided +if [ -z "$DOMAIN_NAME" ]; then + echo "Error: Domain name is required" + show_help exit 1 fi -DOMAIN_NAME="$1" BASE_URL="https://${DOMAIN_NAME}" # Generate a secure random secret for BETTER_AUTH_SECRET @@ -32,13 +109,31 @@ DOMAIN_NAME=${DOMAIN_NAME} BASE_URL=${BASE_URL} BETTER_AUTH_SECRET=${BETTER_AUTH_SECRET} DISABLE_SIGNUP=false +USE_WEBSERVER=${USE_WEBSERVER} +HOST_BACKEND_PORT=${HOST_BACKEND_PORT} +HOST_CLIENT_PORT=${HOST_CLIENT_PORT} EOL echo ".env file created successfully with domain ${DOMAIN_NAME}." +if [ "$USE_WEBSERVER" = "false" ]; then + echo "Caddy webserver is disabled. You'll need to set up your own webserver." + if [ "$BACKEND_PORT" = "3001" ] && [ "$CLIENT_PORT" = "3002" ]; then + echo "The backend service will be available on port 3001 and the client on port 3002." + else + echo "The backend service will be available on port ${BACKEND_PORT} (mapped to container port 3001)" + echo "The client service will be available on port ${CLIENT_PORT} (mapped to container port 3002)" + fi +fi # Build and start the Docker Compose stack echo "Building and starting Docker services..." -docker compose up --build -d +if [ "$USE_WEBSERVER" = "false" ]; then + # Start without the caddy service when using --no-webserver + docker compose up --build -d backend client clickhouse postgres +else + # Start all services including caddy + docker compose up --build -d +fi echo "Setup complete. Services are starting in the background." echo "You can monitor logs with: docker compose logs -f" \ No newline at end of file diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..b21a234 --- /dev/null +++ b/start.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status. +set -e + +echo "Starting services..." + +# Check if .env file exists +if [ ! -f .env ]; then + echo "Error: .env file not found. Please run setup.sh first." + echo "Usage: ./setup.sh " + exit 1 +fi + +# Load environment variables +source .env + +if [ "$USE_WEBSERVER" = "false" ]; then + # Start without the caddy service when using --no-webserver + docker compose start backend client clickhouse postgres +else + # Start all services including caddy + docker compose start +fi + +echo "Services started. You can monitor logs with: docker compose logs -f" \ No newline at end of file diff --git a/stop.sh b/stop.sh new file mode 100644 index 0000000..21c68b7 --- /dev/null +++ b/stop.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status. +set -e + +echo "Stopping services..." +docker compose stop + +echo "Services stopped." \ No newline at end of file diff --git a/update.sh b/update.sh new file mode 100644 index 0000000..c3bfa93 --- /dev/null +++ b/update.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status. +set -e + +echo "Updating to the latest version..." + +# Pull latest changes from git repository +echo "Pulling latest code..." +git pull + +# Stop running containers +echo "Stopping current services..." +docker compose down + +# Check if .env file exists +if [ ! -f .env ]; then + echo "Error: .env file not found. Please run setup.sh first." + echo "Usage: ./setup.sh " + exit 1 +fi + +# Rebuild and start containers +echo "Rebuilding and starting updated services..." + +# Load environment variables +source .env + +if [ "$USE_WEBSERVER" = "false" ]; then + # Start without the caddy service when using --no-webserver + docker compose up --build -d backend client clickhouse postgres +else + # Start all services including caddy + docker compose up --build -d +fi + +echo "Update complete. Services are running with the latest version." +echo "You can monitor logs with: docker compose logs -f" \ No newline at end of file