feat: Add production deployment stack
- docker-compose.yml with Traefik integration - docker-compose.simple.yml for direct port exposure - .env.example with all configuration options - setup.sh script for initial deployment Usage: cd deploy/ cp .env.example .env # Edit .env with your values ./setup.sh docker compose up -d Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
25a67ffea0
commit
411f7d4604
|
|
@ -0,0 +1,44 @@
|
||||||
|
# LetsBe Hub Production Configuration
|
||||||
|
# Copy this file to .env and fill in the values
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# REQUIRED - Must be set before deployment
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Hub public URL (used for auth callbacks and runner communication)
|
||||||
|
HUB_URL=https://hub.yourdomain.com
|
||||||
|
HUB_DOMAIN=hub.yourdomain.com
|
||||||
|
|
||||||
|
# Database password (generate a strong random password)
|
||||||
|
POSTGRES_PASSWORD=CHANGE_ME_STRONG_PASSWORD_HERE
|
||||||
|
|
||||||
|
# NextAuth secret (generate with: openssl rand -base64 32)
|
||||||
|
NEXTAUTH_SECRET=CHANGE_ME_GENERATE_WITH_OPENSSL_RAND_BASE64_32
|
||||||
|
|
||||||
|
# Credential encryption key (generate with: openssl rand -hex 32)
|
||||||
|
CREDENTIAL_ENCRYPTION_KEY=CHANGE_ME_GENERATE_WITH_OPENSSL_RAND_HEX_32
|
||||||
|
|
||||||
|
# Settings encryption key (generate with: openssl rand -hex 32)
|
||||||
|
SETTINGS_ENCRYPTION_KEY=CHANGE_ME_GENERATE_WITH_OPENSSL_RAND_HEX_32
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# OPTIONAL - Defaults are usually fine
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Database settings
|
||||||
|
POSTGRES_USER=letsbe_hub
|
||||||
|
POSTGRES_DB=letsbe_hub
|
||||||
|
|
||||||
|
# Hub image tag (default: master)
|
||||||
|
HUB_IMAGE_TAG=master
|
||||||
|
|
||||||
|
# Ansible Runner settings
|
||||||
|
DOCKER_REGISTRY_URL=code.letsbe.solutions
|
||||||
|
DOCKER_IMAGE_NAME=letsbe/ansible-runner
|
||||||
|
DOCKER_IMAGE_TAG=master
|
||||||
|
DOCKER_MAX_CONCURRENT=3
|
||||||
|
|
||||||
|
# Host paths for job configs (runner containers need access)
|
||||||
|
# These directories will be created automatically by Docker
|
||||||
|
JOBS_HOST_DIR=/opt/letsbe-hub/jobs
|
||||||
|
LOGS_HOST_DIR=/opt/letsbe-hub/logs
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Simpler version without Traefik - exposes port 3000 directly
|
||||||
|
# Use this if you have your own reverse proxy or want direct access
|
||||||
|
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: letsbe-hub-db
|
||||||
|
env_file: .env
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER:-letsbe_hub}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB:-letsbe_hub}
|
||||||
|
volumes:
|
||||||
|
- hub-db-data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-letsbe_hub} -d ${POSTGRES_DB:-letsbe_hub}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
hub:
|
||||||
|
image: code.letsbe.solutions/letsbe/hub:${HUB_IMAGE_TAG:-master}
|
||||||
|
container_name: letsbe-hub-app
|
||||||
|
env_file: .env
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
environment:
|
||||||
|
DATABASE_URL: postgresql://${POSTGRES_USER:-letsbe_hub}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-letsbe_hub}
|
||||||
|
NEXTAUTH_URL: ${HUB_URL}
|
||||||
|
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
||||||
|
AUTH_TRUST_HOST: "true"
|
||||||
|
HUB_URL: ${HUB_URL}
|
||||||
|
CREDENTIAL_ENCRYPTION_KEY: ${CREDENTIAL_ENCRYPTION_KEY}
|
||||||
|
SETTINGS_ENCRYPTION_KEY: ${SETTINGS_ENCRYPTION_KEY}
|
||||||
|
DOCKER_REGISTRY_URL: ${DOCKER_REGISTRY_URL:-code.letsbe.solutions}
|
||||||
|
DOCKER_IMAGE_NAME: ${DOCKER_IMAGE_NAME:-letsbe/ansible-runner}
|
||||||
|
DOCKER_IMAGE_TAG: ${DOCKER_IMAGE_TAG:-master}
|
||||||
|
DOCKER_MAX_CONCURRENT: ${DOCKER_MAX_CONCURRENT:-3}
|
||||||
|
JOBS_HOST_DIR: ${JOBS_HOST_DIR:-/opt/letsbe-hub/jobs}
|
||||||
|
LOGS_HOST_DIR: ${LOGS_HOST_DIR:-/opt/letsbe-hub/logs}
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
- ${JOBS_HOST_DIR:-/opt/letsbe-hub/jobs}:/app/jobs
|
||||||
|
- ${LOGS_HOST_DIR:-/opt/letsbe-hub/logs}:/app/logs
|
||||||
|
user: "0:0"
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
hub-db-data:
|
||||||
|
name: letsbe-hub-db
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: postgres:16-alpine
|
||||||
|
container_name: letsbe-hub-db
|
||||||
|
env_file: .env
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER:-letsbe_hub}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB:-letsbe_hub}
|
||||||
|
volumes:
|
||||||
|
- hub-db-data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-letsbe_hub} -d ${POSTGRES_DB:-letsbe_hub}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- hub-internal
|
||||||
|
|
||||||
|
hub:
|
||||||
|
image: code.letsbe.solutions/letsbe/hub:${HUB_IMAGE_TAG:-master}
|
||||||
|
container_name: letsbe-hub-app
|
||||||
|
env_file: .env
|
||||||
|
environment:
|
||||||
|
# Database
|
||||||
|
DATABASE_URL: postgresql://${POSTGRES_USER:-letsbe_hub}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-letsbe_hub}
|
||||||
|
# Auth
|
||||||
|
NEXTAUTH_URL: ${HUB_URL}
|
||||||
|
NEXTAUTH_SECRET: ${NEXTAUTH_SECRET}
|
||||||
|
AUTH_TRUST_HOST: "true"
|
||||||
|
# Hub URL (for runner callbacks)
|
||||||
|
HUB_URL: ${HUB_URL}
|
||||||
|
# Encryption keys
|
||||||
|
CREDENTIAL_ENCRYPTION_KEY: ${CREDENTIAL_ENCRYPTION_KEY}
|
||||||
|
SETTINGS_ENCRYPTION_KEY: ${SETTINGS_ENCRYPTION_KEY}
|
||||||
|
# Docker spawner config (for ansible runner)
|
||||||
|
DOCKER_REGISTRY_URL: ${DOCKER_REGISTRY_URL:-code.letsbe.solutions}
|
||||||
|
DOCKER_IMAGE_NAME: ${DOCKER_IMAGE_NAME:-letsbe/ansible-runner}
|
||||||
|
DOCKER_IMAGE_TAG: ${DOCKER_IMAGE_TAG:-master}
|
||||||
|
DOCKER_MAX_CONCURRENT: ${DOCKER_MAX_CONCURRENT:-3}
|
||||||
|
# Host paths for job configs (runner containers need access)
|
||||||
|
JOBS_HOST_DIR: ${JOBS_HOST_DIR:-/opt/letsbe-hub/jobs}
|
||||||
|
LOGS_HOST_DIR: ${LOGS_HOST_DIR:-/opt/letsbe-hub/logs}
|
||||||
|
volumes:
|
||||||
|
# Docker socket for spawning runner containers
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
# Job configs (bind mount to host path so runners can access)
|
||||||
|
- ${JOBS_HOST_DIR:-/opt/letsbe-hub/jobs}:/app/jobs
|
||||||
|
- ${LOGS_HOST_DIR:-/opt/letsbe-hub/logs}:/app/logs
|
||||||
|
# Run as root to access Docker socket
|
||||||
|
user: "0:0"
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- hub-internal
|
||||||
|
- traefik
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.hub.rule=Host(`${HUB_DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.hub.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.hub.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.services.hub.loadbalancer.server.port=3000"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
hub-db-data:
|
||||||
|
name: letsbe-hub-db
|
||||||
|
|
||||||
|
networks:
|
||||||
|
hub-internal:
|
||||||
|
traefik:
|
||||||
|
external: true
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# LetsBe Hub Production Setup Script
|
||||||
|
#
|
||||||
|
# Usage: ./setup.sh
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== LetsBe Hub Setup ==="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Check if .env exists
|
||||||
|
if [[ ! -f .env ]]; then
|
||||||
|
echo "ERROR: .env file not found!"
|
||||||
|
echo "Copy .env.example to .env and fill in the values first."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Source .env to get variables
|
||||||
|
source .env
|
||||||
|
|
||||||
|
# Validate required variables
|
||||||
|
REQUIRED_VARS=(
|
||||||
|
"HUB_URL"
|
||||||
|
"HUB_DOMAIN"
|
||||||
|
"POSTGRES_PASSWORD"
|
||||||
|
"NEXTAUTH_SECRET"
|
||||||
|
"CREDENTIAL_ENCRYPTION_KEY"
|
||||||
|
"SETTINGS_ENCRYPTION_KEY"
|
||||||
|
)
|
||||||
|
|
||||||
|
for var in "${REQUIRED_VARS[@]}"; do
|
||||||
|
if [[ -z "${!var}" || "${!var}" == *"CHANGE_ME"* ]]; then
|
||||||
|
echo "ERROR: $var is not set or still has placeholder value"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "1. Creating job directories..."
|
||||||
|
JOBS_DIR="${JOBS_HOST_DIR:-/opt/letsbe-hub/jobs}"
|
||||||
|
LOGS_DIR="${LOGS_HOST_DIR:-/opt/letsbe-hub/logs}"
|
||||||
|
mkdir -p "$JOBS_DIR" "$LOGS_DIR"
|
||||||
|
chmod 755 "$JOBS_DIR" "$LOGS_DIR"
|
||||||
|
echo " Created: $JOBS_DIR"
|
||||||
|
echo " Created: $LOGS_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "2. Logging into Gitea registry..."
|
||||||
|
echo " (You will need your Gitea username and password/token)"
|
||||||
|
docker login code.letsbe.solutions
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "3. Pulling images..."
|
||||||
|
docker pull code.letsbe.solutions/letsbe/hub:${HUB_IMAGE_TAG:-master}
|
||||||
|
docker pull code.letsbe.solutions/letsbe/ansible-runner:${DOCKER_IMAGE_TAG:-master}
|
||||||
|
docker pull postgres:16-alpine
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "4. Creating traefik network (if not exists)..."
|
||||||
|
docker network create traefik 2>/dev/null || true
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
echo "=== Setup Complete ==="
|
||||||
|
echo ""
|
||||||
|
echo "Now run: docker compose up -d"
|
||||||
|
echo ""
|
||||||
|
echo "Then configure Docker Hub and Gitea credentials in:"
|
||||||
|
echo " ${HUB_URL}/admin/settings"
|
||||||
Loading…
Reference in New Issue