Compare commits

..
Author SHA1 Message Date
copilot-swe-agent[bot]andSiumauricio 984cf0b4d4 Add issue template configuration file
Co-authored-by: Siumauricio <47042324+Siumauricio@users.noreply.github.com>
2025-12-19 18:34:42 +00:00
copilot-swe-agent[bot] 5b54bdf266 Initial plan 2025-12-19 18:29:57 +00:00
1748 changed files with 11616 additions and 47891 deletions
+11
View File
@@ -0,0 +1,11 @@
blank_issues_enabled: false
contact_links:
- name: 📚 Documentation
url: https://docs.dokploy.com
about: Check out the official Dokploy documentation for guides and references
- name: 🌐 Website
url: https://dokploy.com
about: Visit the official Dokploy website
- name: 💬 GitHub Discussions
url: https://github.com/Dokploy/dokploy/discussions
about: Join the community discussions for questions and support
+2 -6
View File
@@ -30,8 +30,6 @@ Exemplary blueprint: `blueprints/ghost/`—`docker-compose.yml` exposes port 236
1. **Add/Update Template**:
- **REQUIREMENT**: Service **MUST** be open source. Only add templates for applications with an open-source license (e.g., MIT, Apache, GPL, AGPL). Proprietary or closed-source services are not allowed.
- **Verify Docker Images**: Before using any Docker image in `docker-compose.yml`, verify it exists using `docker manifest inspect <image:tag>` (e.g., `docker manifest inspect docker.io/bitnami/discourse:3.5.0`). This ensures the image is available and prevents deployment failures.
- Create `blueprints/<id>/` (e.g., `ghost`).
- Implement `docker-compose.yml` (single service typical; use volumes for persistence).
- Configure `template.toml`—reference vars in `[config.domains]`, `[config.env]`, `[config.mounts]`.
@@ -47,19 +45,17 @@ No tests in repo—focus on manual validation via scripts and Dokploy deploys. D
## Conventions and Patterns
- **Open Source Requirement**: **ALL services MUST be open source**. Only applications with open-source licenses (MIT, Apache, GPL, AGPL, etc.) are allowed. Proprietary or closed-source services are strictly prohibited.
- **Template IDs**: Lowercase, kebab-case (e.g., `active-pieces`); unique across repo—enforced by dedupe script.
- **Docker Compose**: Minimal—omit `ports` (Dokploy proxies), persistent volumes (e.g., `- db-data:/var/lib/postgresql/data`). Services named after folder (e.g., `ghost` service).
- **Docker Compose**: Minimal—omit `ports` (Dokploy proxies), `restart: unless-stopped`, persistent volumes (e.g., `- db-data:/var/lib/postgresql/data`). Services named after folder (e.g., `ghost` service).
- **template.toml**:
- Variables: `[variables] main_domain = "${domain}"`; use helpers for secrets (`${password:64}`, `${base64:32}`).
- Domains: `[[config.domains]] serviceName = "<service>" port = 80 host = "${main_domain}"` (path="/" optional).
- Env: `[[config.env]]` array of "KEY=VALUE" strings, interpolating vars (e.g., "DB_PASSWORD=${db_pass}").
- **URL Variables**: When environment variables require URLs (e.g., `WEB_URL`, `NEXTAUTH_URL`, `PUBLIC_URL`), **always use HTTP by default** (e.g., `"http://${main_domain}"`). HTTPS should only be used if explicitly required by the application or when using a reverse proxy with SSL termination.
- Mounts: `[[config.mounts]] filePath = "/etc/config" content = """multi-line\ncontent"""`.
- JWT helper: `${jwt:secret_var:payload_var}` for auth tokens; payload as JSON string with `exp: ${timestamps:YYYY-MM-DDTHH:mm:ssZ}`.
- **Meta.json**: Entries as JSON objects; tags array of lowercase strings (e.g., ["monitoring", "database"]); links object with `github`, `website`, `docs`.
- **No Networks**: Rely on Dokploy's isolated deployments—avoid explicit `networks:`.
- **Versions**: Pin images to specific versions in `docker-compose.yml` (e.g., `ghost:5.82.0-alpine`); match in `meta.json.version`. **NEVER use `latest` tag**—it can break templates when upstream images change unexpectedly. **Always verify image exists** using `docker manifest inspect <image:tag>` before committing.
- **Versions**: Pin images to specific versions in `docker-compose.yml` (e.g., `ghost:5.82.0-alpine`); match in `meta.json.version`.
- **Logos**: SVG preferred; size ~128x128; file name in `meta.json.logo` (e.g., "ghost.svg").
Cross-component: Templates are independent and ship as static blueprints/meta.
-163
View File
@@ -1,163 +0,0 @@
name: Audit All Blueprints
# The per-PR validators only look at blueprints a PR touches, which is the right
# call for CI cost but means a template is checked when it lands and then never
# again. When a rule is added or tightened, every existing blueprint that breaks
# it stays broken with nothing surfacing it (see #1047).
#
# This job runs the same validators over ALL blueprints on a schedule, so drift
# is reported instead of accumulating. It never runs on pull_request, so it
# cannot block a contributor for a pre-existing problem they did not introduce.
on:
schedule:
# Mondays, 04:00 UTC
- cron: "0 4 * * 1"
workflow_dispatch:
inputs:
fail_on_findings:
description: "Exit non-zero when findings exist (default: report only)"
type: boolean
default: false
permissions:
contents: read
jobs:
audit:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 20.16.0
- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 8
- name: Install dependencies
run: cd build-scripts && pnpm install
- name: Validate per-template metadata
run: node build-scripts/generate-meta.js --check
- name: Audit every blueprint
id: audit
run: |
# $RUNNER_TEMP is per-job; also cleared explicitly so a re-run or a
# partially-executed earlier step can never leak into the summary.
WORK="${RUNNER_TEMP:-/tmp}"
OUT="$WORK/validator-out.txt"
REPORT="$WORK/audit-report.md"
SUMMARY="$WORK/audit-summary.md"
rm -f "$OUT" "$REPORT" "$SUMMARY"
COMPOSE_FAILED=""
TOML_FAILED=""
for dir in blueprints/*/; do
id="$(basename "$dir")"
# Run unconditionally: a blueprint with no docker-compose.yml or no
# template.toml is itself a finding, and the validators already
# report a missing file clearly. Skipping them would hide exactly
# the blueprints most likely to be broken.
if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts \
--file "../$dir/docker-compose.yml") > "$OUT" 2>&1; then
COMPOSE_FAILED="$COMPOSE_FAILED $id"
{
echo "### \`$id\` — docker-compose.yml"
echo '```'
grep '❌' "$OUT" || cat "$OUT"
echo '```'
} >> "$REPORT"
fi
if ! (cd build-scripts && pnpm exec tsx validate-template.ts \
--dir "../$dir") > "$OUT" 2>&1; then
TOML_FAILED="$TOML_FAILED $id"
{
echo "### \`$id\` — template.toml"
echo '```'
grep '❌' "$OUT" || cat "$OUT"
echo '```'
} >> "$REPORT"
fi
done
COMPOSE_COUNT=$(echo $COMPOSE_FAILED | wc -w | tr -d ' ')
TOML_COUNT=$(echo $TOML_FAILED | wc -w | tr -d ' ')
TOTAL=$(ls -d blueprints/*/ | wc -l | tr -d ' ')
{
echo "## Blueprint audit"
echo ""
echo "| check | failing | of |"
echo "| --- | --- | --- |"
echo "| \`validate-docker-compose.ts\` | $COMPOSE_COUNT | $TOTAL |"
echo "| \`validate-template.ts\` | $TOML_COUNT | $TOTAL |"
echo ""
} > "$SUMMARY"
if [ -f "$REPORT" ]; then
cat "$REPORT" >> "$SUMMARY"
else
echo "No findings." >> "$SUMMARY"
fi
# The step summary caps at ~1MB and truncates silently past it, which
# would hide findings exactly as the backlog grows. Bound what we
# append and point at the artifact for the rest.
SUMMARY_LIMIT=$((512 * 1024))
SUMMARY_BYTES=$(wc -c < "$SUMMARY" | tr -d ' ')
if [ "$SUMMARY_BYTES" -gt "$SUMMARY_LIMIT" ]; then
# Emit the header table only, never a byte-sliced prefix: cutting
# mid-``` leaves an unclosed code fence, which swallows the notice
# below it and makes the truncation invisible in the rendered summary.
{
head -n 7 "$SUMMARY"
echo ""
echo "_Report omitted: $SUMMARY_BYTES bytes exceeds the $SUMMARY_LIMIT byte budget for a job summary._"
echo "_Download the \`blueprint-audit\` artifact for the complete output._"
} >> "$GITHUB_STEP_SUMMARY"
else
cat "$SUMMARY" >> "$GITHUB_STEP_SUMMARY"
fi
# Always published, so the full report survives regardless of size.
cp "$SUMMARY" "$WORK/blueprint-audit.md"
echo "report_path=$WORK/blueprint-audit.md" >> "$GITHUB_OUTPUT"
echo "compose_failed=$COMPOSE_COUNT" >> "$GITHUB_OUTPUT"
echo "toml_failed=$TOML_COUNT" >> "$GITHUB_OUTPUT"
if [ "$COMPOSE_COUNT" != "0" ] || [ "$TOML_COUNT" != "0" ]; then
echo "::warning::$COMPOSE_COUNT compose and $TOML_COUNT template.toml findings across $TOTAL blueprints"
fi
- name: Upload the full report
if: always() && steps.audit.outputs.report_path != ''
uses: actions/upload-artifact@v4
with:
name: blueprint-audit
path: ${{ steps.audit.outputs.report_path }}
retention-days: 30
- name: Fail if requested
# `inputs` is only populated for workflow_dispatch; on a schedule run it
# is not available, so gate on the event first and read the value through
# github.event.inputs, which is an empty string when absent.
if: >-
github.event_name == 'workflow_dispatch' &&
github.event.inputs.fail_on_findings == 'true' &&
(steps.audit.outputs.compose_failed != '0' || steps.audit.outputs.toml_failed != '0')
run: |
echo "❌ Findings present and fail_on_findings was requested."
exit 1
+3
View File
@@ -5,6 +5,9 @@ on:
workflows: [Build Preview Deployment]
types:
- completed
pull_request:
branches:
- canary
permissions:
actions: read
deployments: write
+254 -58
View File
@@ -17,10 +17,13 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-depth: 0 # Necesitamos el historial completo para comparar con base
- name: Set up Docker Compose
run: docker compose version
run: |
echo "🐳 Setting up Docker Compose..."
# Docker Compose V2 viene preinstalado en ubuntu-latest
docker compose version
- name: Set up Node.js
uses: actions/setup-node@v4
@@ -33,89 +36,282 @@ jobs:
version: 8
- name: Install dependencies
run: cd build-scripts && pnpm install
- name: Detect changed blueprints
id: changed
run: |
echo "📦 Installing Node.js dependencies..."
cd build-scripts && pnpm install
- name: Get changed files
id: changed-files
run: |
echo "🔍 Detecting changed files..."
# Obtener la rama base
BASE_SHA=$(git merge-base HEAD origin/${{ github.base_ref }})
# Obtener todos los blueprints que tienen cambios (en docker-compose.yml o template.toml)
CHANGED_BLUEPRINTS=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | \
grep -E 'blueprints/[^/]+/(docker-compose\.yml|template\.toml)$' | \
sed 's|blueprints/\([^/]*\)/.*|\1|' | \
sort -u)
# Encontrar todos los archivos docker-compose.yml y template.toml modificados/agregados
CHANGED_COMPOSE=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | grep -E 'blueprints/.*/docker-compose\.yml$' || true)
CHANGED_TOML=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | grep -E 'blueprints/.*/template\.toml$' || true)
echo "Changed blueprints:"
echo "$CHANGED_BLUEPRINTS" | while read blueprint; do
[ -n "$blueprint" ] && echo " - $blueprint"
done
# Crear lista de directorios únicos que tienen cambios
CHANGED_DIRS=$(echo -e "$CHANGED_COMPOSE\n$CHANGED_TOML" | sed 's|blueprints/\([^/]*\)/.*|\1|' | sort -u)
# Guardar lista de blueprints (una por línea)
echo "blueprints<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_BLUEPRINTS" >> $GITHUB_OUTPUT
echo "Changed compose files:"
echo "$CHANGED_COMPOSE" | while read file; do [ -n "$file" ] && echo " - $file"; done
echo "Changed TOML files:"
echo "$CHANGED_TOML" | while read file; do [ -n "$file" ] && echo " - $file"; done
echo "Changed directories:"
echo "$CHANGED_DIRS" | while read dir; do [ -n "$dir" ] && echo " - $dir"; done
# Guardar para usar en siguientes pasos
echo "compose_files<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_COMPOSE" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "toml_files<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_TOML" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo "directories<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_DIRS" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Validate blueprints
- name: Validate Docker Compose files syntax
id: validate-compose-syntax
run: |
BLUEPRINTS="${{ steps.changed.outputs.blueprints }}"
echo "🔍 Validating Docker Compose files syntax..."
if [ -z "$BLUEPRINTS" ] || [ "$BLUEPRINTS" = "" ]; then
echo "ℹ️ No blueprints changed, skipping validation"
ERROR=0
COMPOSE_FILES="${{ steps.changed-files.outputs.compose_files }}"
if [ -z "$COMPOSE_FILES" ]; then
echo "ℹ️ No docker-compose.yml files changed, skipping validation"
exit 0
fi
ERROR=0
# Convertir a array para evitar problemas con subshells
mapfile -t BLUEPRINT_ARRAY <<< "$BLUEPRINTS"
# Iterar sobre cada blueprint
for blueprint in "${BLUEPRINT_ARRAY[@]}"; do
[ -z "$blueprint" ] && continue
echo "$COMPOSE_FILES" | while read -r compose_file; do
if [ -z "$compose_file" ]; then
continue
fi
BLUEPRINT_PATH="blueprints/$blueprint"
COMPOSE_FILE="$BLUEPRINT_PATH/docker-compose.yml"
TOML_FILE="$BLUEPRINT_PATH/template.toml"
TEMPLATE_DIR=$(dirname "$compose_file")
TEMPLATE_NAME=$(basename "$TEMPLATE_DIR")
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 Validating: $blueprint"
echo "📦 Validating syntax: $TEMPLATE_NAME"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# 1. Validar best practices de docker-compose.yml
if [ ! -f "$COMPOSE_FILE" ]; then
echo "⚠️ WARNING: docker-compose.yml not found"
# Validar sintaxis de docker-compose.yml usando docker compose
echo "🔍 Validating docker-compose.yml syntax..."
if ! docker compose -f "$compose_file" config > /dev/null 2>&1; then
echo "❌ ERROR: docker-compose.yml syntax is invalid in $TEMPLATE_NAME"
echo "Running docker compose config to show errors:"
docker compose -f "$compose_file" config 2>&1 || true
ERROR=1
continue
fi
echo "🔍 Validating docker-compose.yml best practices..."
if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts --file "../$COMPOSE_FILE" --verbose); then
ERROR=1
continue
fi
# 3. Validar template.toml
if [ -f "$TOML_FILE" ]; then
echo "🔍 Validating template.toml..."
if ! (cd build-scripts && pnpm exec tsx validate-template.ts --dir "../$BLUEPRINT_PATH" --verbose); then
ERROR=1
continue
fi
else
echo "⚠️ WARNING: template.toml not found"
ERROR=1
continue
echo "✅ docker-compose.yml syntax is valid"
fi
echo "✅ All validations passed for $blueprint"
# Obtener lista de servicios del compose
SERVICES=$(docker compose -f "$compose_file" config --services 2>/dev/null || echo "")
echo "📋 Services found in docker-compose.yml:"
echo "$SERVICES" | while read service; do
[ -n "$service" ] && echo " - $service"
done
# Guardar servicios para validación posterior
echo "$SERVICES" > "/tmp/${TEMPLATE_NAME}_services.txt"
done
if [ $ERROR -eq 1 ]; then
echo ""
echo "❌ Validation failed for one or more blueprints"
echo "❌ Docker Compose syntax validation failed"
exit 1
else
echo ""
echo "✅ All blueprints validated successfully"
echo "✅ All Docker Compose files have valid syntax"
fi
- name: Validate Docker Compose best practices
id: validate-compose-practices
run: |
echo "🔍 Validating Docker Compose best practices..."
ERROR=0
COMPOSE_FILES="${{ steps.changed-files.outputs.compose_files }}"
if [ -z "$COMPOSE_FILES" ]; then
echo "ℹ️ No docker-compose.yml files changed, skipping validation"
exit 0
fi
# Convert to array to avoid subshell issues with pipe
# This ensures ERROR=1 inside the loop propagates to the parent shell
mapfile -t COMPOSE_ARRAY <<< "$COMPOSE_FILES"
for compose_file in "${COMPOSE_ARRAY[@]}"; do
if [ -z "$compose_file" ]; then
continue
fi
TEMPLATE_DIR=$(dirname "$compose_file")
TEMPLATE_NAME=$(basename "$TEMPLATE_DIR")
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 Validating best practices: $TEMPLATE_NAME"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Validar usando el script de TypeScript
if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts --file "../$compose_file" --verbose); then
ERROR=1
fi
done
if [ $ERROR -eq 1 ]; then
echo ""
echo "❌ Docker Compose best practices validation failed"
exit 1
else
echo ""
echo "✅ All Docker Compose files follow best practices"
fi
- name: Validate template.toml files
id: validate-toml
run: |
echo "🔍 Validating template.toml files..."
ERROR=0
DIRECTORIES="${{ steps.changed-files.outputs.directories }}"
if [ -z "$DIRECTORIES" ]; then
echo "ℹ️ No template directories changed, skipping TOML validation"
exit 0
fi
# Convert to array to avoid subshell issues with pipe
# This ensures ERROR=1 inside the loop propagates to the parent shell
mapfile -t DIRS_ARRAY <<< "$DIRECTORIES"
for template_dir in "${DIRS_ARRAY[@]}"; do
if [ -z "$template_dir" ]; then
continue
fi
TEMPLATE_PATH="blueprints/$template_dir"
TOML_FILE="$TEMPLATE_PATH/template.toml"
if [ ! -f "$TOML_FILE" ]; then
echo "⚠️ WARNING: template.toml not found in $template_dir (might be deleted)"
continue
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📝 Validating: $template_dir/template.toml"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Validar usando el script de TypeScript con tsx
# Ejecutar desde build-scripts para tener acceso a node_modules
if ! (cd build-scripts && pnpm exec tsx validate-template.ts --dir "../$TEMPLATE_PATH" --verbose); then
ERROR=1
fi
done
if [ $ERROR -eq 1 ]; then
echo ""
echo "❌ template.toml validation failed"
exit 1
else
echo ""
echo "✅ All template.toml files are valid"
fi
- name: Test Docker Compose (dry-run)
id: test-compose
run: |
echo "🧪 Testing Docker Compose files (dry-run)..."
ERROR=0
DIRECTORIES="${{ steps.changed-files.outputs.directories }}"
if [ -z "$DIRECTORIES" ]; then
echo "ℹ️ No template directories changed, skipping dry-run test"
exit 0
fi
echo "$DIRECTORIES" | while read -r template_dir; do
if [ -z "$template_dir" ]; then
continue
fi
COMPOSE_FILE="blueprints/$template_dir/docker-compose.yml"
if [ ! -f "$COMPOSE_FILE" ]; then
continue
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "🧪 Testing: $template_dir"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Cambiar al directorio del template para resolver rutas relativas
cd "blueprints/$template_dir"
# Validar que docker-compose puede parsear el archivo completamente
echo "🔍 Running docker compose config (full validation)..."
if docker compose config > /dev/null 2>&1; then
echo "✅ Docker Compose file is fully valid and can be processed"
# Mostrar información útil
echo "📋 Configuration summary:"
docker compose config --services | while read service; do
[ -n "$service" ] && echo " Service: $service"
done
else
echo "❌ ERROR: Docker Compose file failed full validation"
docker compose config 2>&1 || true
ERROR=1
fi
cd - > /dev/null
done
if [ $ERROR -eq 1 ]; then
echo ""
echo "❌ Docker Compose dry-run test failed"
exit 1
else
echo ""
echo "✅ All Docker Compose files passed dry-run test"
fi
- name: Summary
if: always()
run: |
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📊 Validation Summary"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if [ "${{ steps.validate-compose-syntax.outcome }}" == "success" ] && \
[ "${{ steps.validate-compose-practices.outcome }}" == "success" ] && \
[ "${{ steps.validate-toml.outcome }}" == "success" ] && \
[ "${{ steps.test-compose.outcome }}" == "success" ]; then
echo "✅ All validations passed!"
echo ""
echo "Your Docker Compose and template.toml files are valid and ready to merge."
else
echo "❌ Some validations failed. Please review the errors above."
echo ""
echo "Common issues to check:"
echo " - docker-compose.yml syntax errors"
echo " - template.toml syntax errors"
echo " - serviceName in template.toml must match service names in docker-compose.yml"
echo " - Avoid using container_name, explicit networks, or port mappings"
fi
+60 -21
View File
@@ -1,18 +1,12 @@
name: Validate Template Metadata
name: Validate and Process Meta.json
on:
push:
branches: [canary]
paths:
- "blueprints/**"
- "build-scripts/generate-meta.js"
- "meta.json"
paths: ["meta.json"]
pull_request:
branches: [canary]
paths:
- "blueprints/**"
- "build-scripts/generate-meta.js"
- "meta.json"
paths: ["meta.json"]
workflow_dispatch:
jobs:
@@ -23,19 +17,64 @@ jobs:
- name: Checkout repository
uses: actions/checkout@v4
- name: Reject root meta.json
run: |
if [ -f "meta.json" ]; then
echo "❌ The root meta.json is generated at build time and must not be committed."
echo " Add your template's metadata to blueprints/<id>/meta.json instead."
exit 1
fi
echo "✅ No root meta.json present"
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "18"
- name: Validate per-template metadata
run: node build-scripts/generate-meta.js --check
- name: Validate meta.json structure
run: |
echo "🔍 Validating meta.json structure..."
node -e "
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('meta.json', 'utf8'));
if (!Array.isArray(data)) throw new Error('meta.json must be an array');
console.log('✅ meta.json structure is valid');
console.log('📊 Found', data.length, 'entries');
"
- name: Check for duplicates and sort order
run: |
echo "🔍 Checking for duplicates and sort order..."
node build-scripts/process-meta.js --verbose --output /tmp/meta-test.json
- name: Compare with original
run: |
echo "🔍 Comparing processed file with original..."
if ! diff -q meta.json /tmp/meta-test.json > /dev/null; then
echo "⚠️ meta.json needs processing (duplicates found or not sorted)"
echo "Original entries:"
node -e "console.log(JSON.parse(require('fs').readFileSync('meta.json', 'utf8')).length)"
echo "Processed entries:"
node -e "console.log(JSON.parse(require('fs').readFileSync('/tmp/meta-test.json', 'utf8')).length)"
echo ""
echo "To fix this, run: npm run process-meta"
exit 1
else
echo "✅ meta.json is properly deduplicated and sorted"
fi
- name: Validate required fields
run: |
echo "🔍 Validating required fields..."
node -e "
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('meta.json', 'utf8'));
const required = ['id', 'name', 'version', 'description', 'links', 'logo', 'tags'];
let issues = 0;
data.forEach((item, index) => {
const missing = required.filter(field => !item[field]);
if (missing.length > 0) {
console.log('❌ Entry', index, '(' + item.id + '):', 'Missing fields:', missing.join(', '));
issues++;
}
});
if (issues > 0) {
console.log('🚨 Found', issues, 'entries with missing required fields');
process.exit(1);
} else {
console.log('✅ All entries have required fields');
}
"
+129 -6
View File
@@ -49,10 +49,133 @@ jobs:
echo "✅ Blueprint folders validated successfully."
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Validate meta.json structure and required fields
run: |
echo "🔍 Validating meta.json structure and required fields..."
- name: Validate per-template metadata (blueprints/<id>/meta.json)
run: node build-scripts/generate-meta.js --check
# First check if meta.json exists and is valid JSON
if [ ! -f "meta.json" ]; then
echo "❌ meta.json file not found"
exit 1
fi
if ! jq empty meta.json 2>/dev/null; then
echo "❌ meta.json is not a valid JSON file"
exit 1
fi
ERROR=0
ERRORS_FOUND=""
# Debug: Show total number of entries
TOTAL_ENTRIES=$(jq '. | length' meta.json)
echo "📊 Total entries in meta.json: $TOTAL_ENTRIES"
# Get all entries at once and process them
TOTAL_INDEX=$(($TOTAL_ENTRIES - 1))
for i in $(seq 0 $TOTAL_INDEX); do
entry=$(jq -c ".[$i]" meta.json)
INDEX=$((i + 1))
echo "-------------------------------------------"
echo "🔍 Checking entry #$INDEX..."
# Get the ID for better error reporting
ID=$(echo "$entry" | jq -r '.id // "UNKNOWN"')
echo "📝 Processing entry with ID: $ID"
# Validate required top-level fields
for field in "id" "name" "version" "description" "logo" "links" "tags"; do
if [ "$(echo "$entry" | jq "has(\"$field\")")" != "true" ]; then
ERROR_MSG="❌ Entry #$INDEX (ID: $ID) is missing required field: $field"
echo "$ERROR_MSG"
ERRORS_FOUND="${ERRORS_FOUND}${ERROR_MSG}\n"
ERROR=1
fi
done
# Validate links object required fields
if [ "$(echo "$entry" | jq 'has("links")')" == "true" ]; then
for link_field in "github" "website" "docs"; do
if [ "$(echo "$entry" | jq ".links | has(\"$link_field\")")" != "true" ]; then
ERROR_MSG="❌ Entry #$INDEX (ID: $ID): links object is missing required field: $link_field"
echo "$ERROR_MSG"
ERRORS_FOUND="${ERRORS_FOUND}${ERROR_MSG}\n"
ERROR=1
fi
done
fi
# Validate tags array is not empty
if [ "$(echo "$entry" | jq '.tags | length')" -eq 0 ]; then
ERROR_MSG="❌ Entry #$INDEX (ID: $ID): tags array cannot be empty"
echo "$ERROR_MSG"
ERRORS_FOUND="${ERRORS_FOUND}${ERROR_MSG}\n"
ERROR=1
fi
done
echo "-------------------------------------------"
if [ $ERROR -eq 1 ]; then
echo "❌ meta.json structure validation failed."
echo -e "Summary of all errors found:${ERRORS_FOUND}"
exit 1
else
echo "✅ meta.json structure validated successfully."
fi
- name: Validate meta.json matches blueprint folders and logo files
run: |
echo "🔍 Validating meta.json against blueprint folders and logos..."
ERROR=0
# Read all blueprint folder names into an array
FOLDERS=($(ls -1 blueprints))
# Extract ids and logos from meta.json
IDS_AND_LOGOS=$(jq -c '.[] | {id, logo}' meta.json)
# Validate each id in meta.json exists as a folder
for item in $IDS_AND_LOGOS; do
ID=$(echo "$item" | jq -r '.id')
LOGO=$(echo "$item" | jq -r '.logo')
# Check if folder exists
if [ ! -d "blueprints/$ID" ]; then
echo "❌ meta.json id \"$ID\" does not have a matching folder in blueprints/"
ERROR=1
continue
fi
# Check if logo file exists inside its folder
if [ ! -f "blueprints/$ID/$LOGO" ]; then
echo "❌ Logo \"$LOGO\" defined for \"$ID\" does not exist in blueprints/$ID/"
ERROR=1
fi
done
# Validate each folder has a matching id in meta.json
META_IDS=$(jq -r '.[].id' meta.json)
for FOLDER in "${FOLDERS[@]}"; do
FOUND=0
for ID in $META_IDS; do
if [ "$FOLDER" == "$ID" ]; then
FOUND=1
break
fi
done
if [ "$FOUND" -eq 0 ]; then
echo "❌ Folder \"$FOLDER\" has no matching id in meta.json"
ERROR=1
fi
done
if [ $ERROR -eq 1 ]; then
echo "❌ meta.json validation failed."
exit 1
else
echo "✅ meta.json validated successfully."
fi
+1 -3
View File
@@ -1,3 +1 @@
node_modules
package-lock.json
meta.json.backup.*
node_modules
+48 -34
View File
@@ -29,9 +29,9 @@ This document provides essential context for AI models interacting with this pro
- `/src/hooks`: Custom React hooks (e.g., `useFuseSearch.ts` for fuzzy search).
- `/src/store`: Zustand state management store.
- `/src/lib`: Utility functions and helpers.
- `/build-scripts`: Metadata tooling — `generate-meta.js` (aggregates and validates every `blueprints/<id>/meta.json` into the served `meta.json` artifact) and `explode-meta.js` (one-shot migration reference).
- `/build-scripts`: Advanced meta.json processing tools with CLI options and JSON schema validation.
- `/.github/workflows`: CI/CD workflows for validation, preview builds, and deployments.
- Root level: There is NO root `meta.json` in the repo — each template's metadata lives in `blueprints/<id>/meta.json` and the global registry is generated at build time.
- Root level: Core processing scripts (`dedupe-and-sort-meta.js`), metadata index (`meta.json`), build configuration (`Makefile`, `package.json`).
- **Module Organization:** Node.js scripts for metadata processing, React components for frontend UI, Docker Compose files for service definitions, TOML files for Dokploy configuration. Frontend uses component-based architecture with hooks for business logic and Zustand for global state.
## 4. Coding Conventions & Style Guide
@@ -42,7 +42,7 @@ This document provides essential context for AI models interacting with this pro
- React components: PascalCase (`TemplateGrid`, `SearchBar`)
- Constants: SCREAMING_SNAKE_CASE (`MAX_BUFFER_SIZE`)
- Template IDs: lowercase, kebab-case (`activepieces`, `ghost`) - **MUST** be unique across repository
- Files: kebab-case for scripts (`generate-meta.js`), PascalCase for React components (`TemplateGrid.tsx`), kebab-case for directories (`build-scripts`)
- Files: snake_case for scripts (`dedupe-and-sort-meta.js`), PascalCase for React components (`TemplateGrid.tsx`), kebab-case for directories (`build-scripts`)
- Docker services: **MUST** match blueprint folder name exactly (e.g., `ghost` service in `blueprints/ghost/`)
- **API Design:**
- **Backend:** Procedural scripting approach with CLI interfaces. Template system uses declarative configuration over imperative code.
@@ -65,15 +65,17 @@ This document provides essential context for AI models interacting with this pro
## 5. Key Files & Entrypoints
- **Main Entrypoints:**
- **Backend:** `build-scripts/generate-meta.js` - Aggregates and validates all `blueprints/<id>/meta.json` files into the served `meta.json` artifact (`app/public/meta.json`, gitignored).
- **Backend:** `dedupe-and-sort-meta.js` - Primary script for processing meta.json file.
- **Frontend:** `app/src/main.tsx` - React application entry point.
- **Configuration:**
- `app/package.json` - Frontend dependencies and pnpm scripts (`dev` and `build` run `generate-meta.js` first).
- `blueprints/<id>/meta.json` - Per-template metadata (one object per template; 400+ templates).
- `package.json` - Root Node.js project configuration and npm scripts.
- `app/package.json` - Frontend dependencies and pnpm scripts.
- `meta.json` - Centralized template registry (200+ entries).
- `Makefile` - Build automation with targets for processing, validation, and cleanup.
- `app/vite.config.ts` - Vite build configuration with static copy plugin.
- `app/tsconfig.json` - TypeScript compiler configuration.
- **CI/CD Pipeline:**
- `.github/workflows/validate-meta.yml` - Validates every `blueprints/<id>/meta.json` (via `generate-meta.js --check`) and rejects a committed root `meta.json`.
- `.github/workflows/validate-meta.yml` - Validates meta.json structure, duplicates, and sort order.
- `.github/workflows/build-preview.yml` - Builds preview deployments for PRs.
- `.github/workflows/deploy-preview.yml` - Deploys previews to Cloudflare Pages.
@@ -90,26 +92,47 @@ This document provides essential context for AI models interacting with this pro
cd app && pnpm install
```
- **Validate template metadata** (CRITICAL: run after adding/editing any `blueprints/<id>/meta.json`)
- **Process meta.json** (CRITICAL: Run after ANY meta.json edits)
```bash
node build-scripts/generate-meta.js --check
npm run process-meta
# or
make process-meta
# or
node dedupe-and-sort-meta.js
```
- **Generate the served meta.json artifact** (done automatically by `pnpm dev` / `pnpm build` in `app/`)
- **Validate without modifying**
```bash
node build-scripts/generate-meta.js # writes app/public/meta.json
npm run validate-meta
# or
make validate
```
- **Quick check for duplicates/sort status**
```bash
make check
```
- **Clean backup files**
```bash
make clean
```
- **Task Configuration:**
- **App scripts (`app/package.json`, pnpm):**
- `dev`: Generate meta artifact + start Vite dev server
- `build`: Generate meta artifact + typecheck + Vite build
- `generate-meta`: Generate the meta artifact only
- **NPM Scripts:** Run `npm run` to list available scripts. Key scripts:
- `process-meta`: Remove duplicates and sort meta.json
- `process-meta-verbose`: Process with detailed output
- `validate-meta`: Validate structure without changes
- **Makefile Targets:** Run `make help` to list targets. Key targets:
- `process-meta`: Process meta.json
- `validate`: Validate without modifying
- `check`: Quick duplicate/sort check
- `build`: Full build process
- `clean`: Remove backup files
- **Testing:** No formal unit testing framework. Validation occurs through:
- Script-based validation of every `blueprints/<id>/meta.json` (`build-scripts/generate-meta.js --check`)
- Script-based validation of meta.json structure and schema
- Manual testing via Dokploy preview deployments (import base64 from PR preview)
- JSON schema validation in `build-scripts/process-meta.js`
- TypeScript compilation errors caught during build
- ESLint for code quality in frontend
- **CI/CD Process:**
- **On blueprint changes:** Validates each template's metadata (required fields, id/folder match, logo file exists) and rejects a committed root `meta.json`.
- **On meta.json changes:** Validates structure, checks for duplicates, verifies sort order, compares processed vs original.
- **On PR creation:** Builds preview deployment, generates base64 import for testing in Dokploy.
- **Preview testing:** Use PR description link → Search template → Copy base64 → Import in Dokploy instance.
@@ -117,18 +140,19 @@ This document provides essential context for AI models interacting with this pro
- **Contribution Guidelines:**
- Follow existing Dokploy template structure strictly. Each blueprint must be independent with no shared state.
- **CRITICAL:** Template metadata lives in `blueprints/<id>/meta.json` (one object per template). Never create or edit a root-level `meta.json` — it is generated at build time. Run `node build-scripts/generate-meta.js --check` after any metadata edits.
- **CRITICAL:** Always run `node dedupe-and-sort-meta.js` or `npm run process-meta` after ANY meta.json edits.
- Test templates in Dokploy preview before submitting PRs (use base64 import from PR preview).
- Add logo file (SVG preferred, ~128x128px) to blueprint folder.
- Ensure the `id` in `blueprints/<id>/meta.json` exactly matches the blueprint folder name (lowercase kebab-case).
- Ensure template `id` in meta.json exactly matches blueprint folder name (lowercase kebab-case).
- **Docker Compose Conventions (CRITICAL):**
- **Version:** MUST be `3.8`
- **NEVER include:** `ports` (use `expose` only), `container_name`, `networks` (Dokploy handles isolation)
- **ALWAYS include:** `restart: unless-stopped` or `restart: always`, persistent volumes
- **Service naming:** MUST match blueprint folder name exactly
- **Image source:** Prefer pinned Docker images from registries (`image: ghost:6-alpine`). When no official published images are available, use Docker Compose `build` with a Git context (e.g., `context: "https://github.com/..."#main`, see `blueprints/opendots` for an example building both `app` and `browser` targets from the upstream repository).
- **Example (with pinned image):**
- **Example:**
```yaml
version: "3.8"
services:
ghost:
image: ghost:6-alpine
@@ -146,22 +170,12 @@ This document provides essential context for AI models interacting with this pro
- **Env:** Array of strings: `env = ["KEY=VALUE", "DB_PASSWORD=${db_pass}"]`
- **Available helpers:** `${domain}`, `${password:length}`, `${base64:length}`, `${hash:length}`, `${uuid}`, `${randomPort}`, `${email}`, `${username}`, `${timestamp}`, `${timestamps:datetime}`, `${timestampms:datetime}`, `${jwt:secret_var:payload_var}`
- **JWT helper example:** `${jwt:mysecret:mypayload}` with payload containing `exp: ${timestamps:2030-01-01T00:00:00Z}`
- **Volume bind mounts (in docker-compose.yml):** When mounting host paths, do NOT use absolute paths like `"/folder:/path/in/container"`. Use relative paths instead:
```yaml
# Invalid
volumes:
- "/folder:/path/in/container" ❌
# Valid
volumes:
- "../files/my-database:/var/lib/mysql" ✅
- "../files/my-configs:/etc/my-app/config" ✅
```
- **meta.json Requirements:**
- **Required fields:** `id`, `name`, `version`, `description`, `links` (with `github`), `logo`, `tags` (array)
- **Tags:** Lowercase strings (e.g., `["monitoring", "database"]`)
- **Version:** MUST match Docker image version in docker-compose.yml when using `image:`. When building from source (using `build:` with a Git context), the version should reflect the template's own version or the upstream project's version (e.g., `"latest"` for tracking `main` branch).
- **Version:** MUST match Docker image version in docker-compose.yml
- **Logo:** Filename only (e.g., `"ghost.jpeg"`), file must exist in blueprint folder
- **Frontend Development:**
@@ -182,8 +196,8 @@ This document provides essential context for AI models interacting with this pro
- **Dependencies:**
- When adding new templates, prefer pinned Docker images to specific versions when available
- Update meta.json with exact version matching Docker Compose image version (for `image:`), or the template/upstream version (for `build:` from source)
- When adding new templates, ensure Docker images are pinned to specific versions
- Update meta.json with exact version matching Docker Compose image version
- For frontend dependencies: Use `pnpm add <package>` in `app/` directory
- For root dependencies: Use `npm install <package>` in root directory
+8 -11
View File
@@ -20,11 +20,6 @@ This project adheres to the [Contributor Covenant Code of Conduct](https://www.c
For larger changes or questions, open an issue first to discuss your ideas.
### Pull Request Guidelines
- **Keep PRs small and focused.** Avoid very large PRs; prefer several smaller PRs (e.g., one template or one logical change per PR). This speeds up review and keeps the history clear.
- **Test before submitting.** Any PR that has not been tested by the contributor will be closed. This keeps the PR queue tidy and ensures that only contributions that have been verified by their authors are considered.
## Adding a New Template
To add a new template, follow these steps:
@@ -32,11 +27,13 @@ To add a new template, follow these steps:
1. **Create a Folder in `blueprints/`**: Name it after the template (e.g., `grafana`). Use lowercase with hyphens for multi-word names.
2. **Add `docker-compose.yml`**: Define the services, volumes, and other Compose configurations. Key guidelines:
- Use `version: "3.8"` or later.
- Avoid exposing ports explicitly (e.g., no `ports: - "3000:3000"`; just `- 3000` if needed for internal reference).
- Do not use `container_name`.
- Avoid custom networks like `dokploy-network`; Dokploy handles isolation automatically.
- Example for Grafana:
```yaml
version: "3.8"
services:
grafana:
image: grafana/grafana-enterprise:9.5.20
@@ -71,16 +68,16 @@ To add a new template, follow these steps:
"""
```
4. **Add `meta.json` in your template folder**: Create `blueprints/<id>/meta.json` with a single JSON object describing your template. Do NOT create or edit a root-level `meta.json` — the global registry is generated automatically at build time from every `blueprints/<id>/meta.json` (this is what makes template PRs conflict-free). Include:
4. **Update `meta.json`**: Add an entry for your template in the root `meta.json`. Include:
- `id`: Folder name (e.g., "grafana") — must match the folder exactly.
- `id`: Folder name (e.g., "grafana").
- `name`: Display name.
- `version`: Image/tag version.
- `description`: Brief overview.
- `logo`: Filename of the logo (e.g., "grafana.svg").
- `links`: Object with `github`, `website`, `docs` (URLs; use `""` if not applicable).
- `links`: Object with `github`, `website`, `docs` (URLs).
- `tags`: Array of categories (e.g., ["monitoring"]).
- Example (`blueprints/grafana/meta.json`):
- Example:
```json
{
"id": "grafana",
@@ -99,7 +96,7 @@ To add a new template, follow these steps:
5. **Add a Logo**: Place an SVG or PNG logo (e.g., `grafana.svg`) in the template folder. Keep it simple and representative (ideally 512x512 or similar).
6. **Run Validation**: Before pushing, run `node build-scripts/generate-meta.js --check` to validate every template's metadata (same check CI runs), and manually check for syntax errors in YAML/TOML.
6. **Run Validation**: Before pushing, run `node dedupe-and-sort-meta.js` (if available) or manually check for syntax errors in YAML/TOML/JSON.
7. **Commit and PR**: Push your branch and open a PR. Include:
- A description of the template.
@@ -193,7 +190,7 @@ If issues arise, debug locally or in the preview. Fix and update your PR.
## Updating Existing Templates
- Follow the same structure as adding new ones.
- Bump `version` in the template's `blueprints/<id>/meta.json` and update `docker-compose.yml` image tags.
- Bump `version` in `meta.json` and update `docker-compose.yml` image tags.
- Test thoroughly, as changes may affect users.
## Reporting Bugs or Requesting Features
+5 -4
View File
@@ -8,7 +8,7 @@ This is the official repository for the Dokploy Open Source Templates.
1. Fork the repository
2. Create a new branch
3. Add the template to the `blueprints` folder (`docker-compose.yml`, `template.toml`)
4. Add the template metadata (name, description, version, logo, links, tags) in a `meta.json` file inside your template folder (`blueprints/<id>/meta.json`) — do NOT create or edit a root-level `meta.json`, it is generated at build time
4. Add the template metadata (name, description, version, logo, links, tags) to the `meta.json` file
5. Add the logo to the template folder
6. Commit and push your changes
7. Create a pull request (PR)
@@ -35,6 +35,7 @@ Let's suppose you want to add the [Grafana](https://grafana.com/) template to th
2. Add the `docker-compose.yml` file to the folder
```yaml
version: "3.8"
services:
grafana:
image: grafana/grafana-enterprise:9.5.20
@@ -61,7 +62,7 @@ host = "${main_domain}"
[[config.mounts]]
```
4. Add the meta information in `blueprints/<id>/meta.json` (a single JSON object; the root `meta.json` is generated automatically at build time)
4. Add meta information to the `meta.json` file in the root folder
```json
{
@@ -182,7 +183,7 @@ services:
- 3000:3000
# Instead use this way:
expose:
ports:
- 3000
```
@@ -224,4 +225,4 @@ services:
6. Now you can click on the Deploy Button and wait for the deployment to finish, and try to access to the service, if everything is correct you should access to the service and see the template working.
Each template's metadata lives in `blueprints/<id>/meta.json`; the global `meta.json` is generated at build time. Use `node build-scripts/generate-meta.js --check` to validate all template metadata.
use the command `node dedupe-and-sort-meta.js` to deduplicate and sort the meta.json file.
+1 -10
View File
@@ -4,16 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="canonical" href="https://dokploy.com/templates" />
<meta name="description" content="Browse 500+ open source Docker templates and one-click self-hosted apps to deploy with Dokploy – databases, CMS, analytics, AI tools and more." />
<meta property="og:title" content="Dokploy Templates: 500+ Open Source Apps" />
<meta property="og:description" content="Browse 500+ open source Docker templates and one-click self-hosted apps to deploy with Dokploy – databases, CMS, analytics, AI tools and more." />
<meta property="og:type" content="website" />
<meta property="og:url" content="https://templates.dokploy.com" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Dokploy Templates: 500+ Open Source Apps" />
<meta name="twitter:description" content="Browse 500+ open source Docker templates and one-click self-hosted apps to deploy with Dokploy – databases, CMS, analytics, AI tools and more." />
<title>Dokploy Templates: 500+ Open Source Apps</title>
<title>Dokploy Blueprints</title>
</head>
<body>
<div id="root"></div>
+6 -7
View File
@@ -4,11 +4,10 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "node ../build-scripts/generate-meta.js && vite",
"build": "node ../build-scripts/generate-meta.js && tsc -b && vite build",
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
"generate-meta": "node ../build-scripts/generate-meta.js"
"preview": "vite preview"
},
"dependencies": {
"@codemirror/autocomplete": "^6.19.1",
@@ -35,13 +34,13 @@
"next-themes": "^0.4.6",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.18.0",
"react-router-dom": "^7.9.4",
"sonner": "^2.0.7",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.16",
"tailwindcss-animate": "^1.0.7",
"vite-plugin-static-copy": "^2.3.2",
"yaml": "2.8.3",
"yaml": "2.7.1",
"zustand": "^5.0.8"
},
"devDependencies": {
@@ -51,6 +50,6 @@
"@vitejs/plugin-react": "^4.7.0",
"globals": "^15.15.0",
"typescript": "~5.7.3",
"vite": "^6.4.3"
"vite": "^6.4.1"
}
}
+284 -350
View File
File diff suppressed because it is too large Load Diff
-4
View File
@@ -1,4 +0,0 @@
packages:
- .
allowBuilds:
esbuild: false
-1
View File
@@ -1 +0,0 @@
meta.json
+1 -3
View File
@@ -13,9 +13,7 @@ const Navigation = () => {
"https://api.github.com/repos/dokploy/dokploy"
);
const data = await response.json();
setGithubStars(
Number.isFinite(data.stargazers_count) ? data.stargazers_count : 0
);
setGithubStars(data.stargazers_count);
} catch (error) {
console.error("Error fetching GitHub stars:", error);
}
+15 -17
View File
@@ -109,22 +109,20 @@ const Search = () => {
</div>
</div>
<div className="relative">
<Input
type="text"
placeholder="Search templates..."
value={searchQuery}
onChange={handleSearchChange}
className="w-full"
/>
{searchQuery.length > 0 ? (
<div className="cursor-pointer" onClick={handleClearSearch}>
<XIcon className="absolute end-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
</div>
) : (
<SearchIcon className="absolute end-3 top-1/2 -translate-y-1/2 lg:size-5 size-4 text-gray-400" />
)}
</div>
<Input
type="text"
placeholder="Search templates..."
value={searchQuery}
onChange={handleSearchChange}
className="w-full p-6"
/>
{searchQuery.length > 0 ? (
<div className="cursor-pointer" onClick={handleClearSearch}>
<XIcon className="absolute end-3 translate-y-3.5 top-1/2 h-5 w-5 text-gray-400" />
</div>
) : (
<SearchIcon className="absolute end-3 translate-y-2 top-1/2 lg:size-5 size-4 text-gray-400" />
)}
</div>
<div className="flex flex-row gap-2 justify-between">
@@ -156,7 +154,7 @@ const Search = () => {
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-50 p-0">
<PopoverContent className="w-[200px] p-0">
<Command shouldFilter={false}>
<CommandInput
placeholder="Search tags...."
+1 -36
View File
@@ -14,7 +14,6 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs";
import { Label } from "./ui/label";
import { Clipboard } from "lucide-react";
import { Input } from "./ui/input";
import Markdown from "./ui/markdown";
interface Template {
id: string;
@@ -33,7 +32,6 @@ interface Template {
interface TemplateFiles {
dockerCompose: string | null;
config: string | null;
instructions: string | null;
}
interface TemplateDialogProps {
@@ -171,23 +169,8 @@ const TemplateDialog: React.FC<TemplateDialogProps> = ({
</div>
</div>
)}
<Tabs
defaultValue={
templateFiles?.instructions
? "instructions"
: "docker-compose"
}
className="w-full"
>
<Tabs defaultValue="docker-compose" className="w-full">
<TabsList className="w-full justify-start">
{templateFiles?.instructions && (
<TabsTrigger
value="instructions"
className="data-[state=active]:font-bold"
>
Instructions
</TabsTrigger>
)}
<TabsTrigger
value="docker-compose"
className="data-[state=active]:font-bold"
@@ -201,24 +184,6 @@ const TemplateDialog: React.FC<TemplateDialogProps> = ({
Configuration
</TabsTrigger>
</TabsList>
{templateFiles?.instructions && (
<TabsContent value="instructions" className="mt-4">
<div className="space-y-2">
<Label className="flex flex-col items-start w-fit justify-start gap-1">
<span className="leading-tight text-xl font-semibold">
Setup Instructions
</span>
<span className="leading-tight text-sm text-gray-500">
instructions.md
</span>
</Label>
<div className="rounded-md border p-4">
<Markdown content={templateFiles.instructions} />
</div>
</div>
</TabsContent>
)}
<TabsContent value="docker-compose" className="mt-4">
{templateFiles?.dockerCompose && (
<div className="space-y-2">
+6 -21
View File
@@ -28,7 +28,6 @@ interface Template {
interface TemplateFiles {
dockerCompose: string | null;
config: string | null;
instructions: string | null;
}
interface TemplateGridProps {
@@ -81,34 +80,20 @@ const TemplateGrid: React.FC<TemplateGridProps> = ({ view }) => {
const fetchTemplateFiles = async (templateId: string) => {
setModalLoading(true);
try {
const [dockerComposeRes, configRes, instructionsRes] = await Promise.all(
[
fetch(`/blueprints/${templateId}/docker-compose.yml`),
fetch(`/blueprints/${templateId}/template.toml`),
fetch(`/blueprints/${templateId}/instructions.md`),
]
);
const [dockerComposeRes, configRes] = await Promise.all([
fetch(`/blueprints/${templateId}/docker-compose.yml`),
fetch(`/blueprints/${templateId}/template.toml`),
]);
const dockerCompose = dockerComposeRes.ok
? await dockerComposeRes.text()
: null;
const config = configRes.ok ? await configRes.text() : null;
// Guard against SPA fallbacks that return index.html with a 200 status
// for templates that don't have an instructions.md file.
const instructionsIsMarkdown =
instructionsRes.ok &&
!(instructionsRes.headers.get("content-type") || "").includes(
"text/html"
);
const instructions = instructionsIsMarkdown
? await instructionsRes.text()
: null;
setTemplateFiles({ dockerCompose, config, instructions });
setTemplateFiles({ dockerCompose, config });
} catch (err) {
console.error("Error fetching template files:", err);
setTemplateFiles({ dockerCompose: null, config: null, instructions: null });
setTemplateFiles({ dockerCompose: null, config: null });
} finally {
setModalLoading(false);
}
-177
View File
@@ -1,177 +0,0 @@
import React from "react";
// Minimal markdown renderer (no external dependencies) for the per-template
// instructions.md files. Supports headings, paragraphs, fenced code blocks,
// ordered/unordered lists, and inline code / bold / links.
const INLINE_PATTERN =
/(`[^`]+`)|(\*\*[^*]+\*\*)|(\[[^\]]+\]\([^)\s]+\))/g;
const renderInline = (text: string): React.ReactNode[] => {
const nodes: React.ReactNode[] = [];
let lastIndex = 0;
let key = 0;
let match: RegExpExecArray | null;
INLINE_PATTERN.lastIndex = 0;
while ((match = INLINE_PATTERN.exec(text)) !== null) {
if (match.index > lastIndex) {
nodes.push(text.slice(lastIndex, match.index));
}
const token = match[0];
if (token.startsWith("`")) {
nodes.push(
<code
key={key++}
className="px-1.5 py-0.5 rounded bg-muted font-mono text-[0.85em]"
>
{token.slice(1, -1)}
</code>
);
} else if (token.startsWith("**")) {
nodes.push(<strong key={key++}>{token.slice(2, -2)}</strong>);
} else {
const link = /^\[([^\]]+)\]\(([^)\s]+)\)$/.exec(token);
if (link) {
nodes.push(
<a
key={key++}
href={link[2]}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 dark:text-blue-400 underline underline-offset-2"
>
{link[1]}
</a>
);
} else {
nodes.push(token);
}
}
lastIndex = match.index + token.length;
}
if (lastIndex < text.length) {
nodes.push(text.slice(lastIndex));
}
return nodes;
};
const HEADING_CLASSES: Record<number, string> = {
1: "text-2xl font-bold mt-2",
2: "text-xl font-semibold mt-6 border-b pb-1",
3: "text-lg font-semibold mt-4",
4: "text-base font-semibold mt-3",
};
interface MarkdownProps {
content: string;
}
const Markdown: React.FC<MarkdownProps> = ({ content }) => {
const lines = content.replace(/\r\n/g, "\n").split("\n");
const blocks: React.ReactNode[] = [];
let key = 0;
let i = 0;
while (i < lines.length) {
const line = lines[i];
// Blank line
if (line.trim() === "") {
i++;
continue;
}
// Fenced code block
if (line.trim().startsWith("```")) {
const code: string[] = [];
i++;
while (i < lines.length && !lines[i].trim().startsWith("```")) {
code.push(lines[i]);
i++;
}
i++; // skip closing fence
blocks.push(
<pre
key={key++}
className="bg-muted rounded-md p-4 overflow-x-auto text-sm font-mono whitespace-pre"
>
<code>{code.join("\n")}</code>
</pre>
);
continue;
}
// Heading
const heading = /^(#{1,4})\s+(.*)$/.exec(line);
if (heading) {
const level = heading[1].length;
const Tag = `h${Math.min(level + 1, 6)}` as keyof React.JSX.IntrinsicElements;
blocks.push(
<Tag key={key++} className={HEADING_CLASSES[level]}>
{renderInline(heading[2])}
</Tag>
);
i++;
continue;
}
// Unordered list
if (/^[-*]\s+/.test(line)) {
const items: string[] = [];
while (i < lines.length && /^[-*]\s+/.test(lines[i])) {
items.push(lines[i].replace(/^[-*]\s+/, ""));
i++;
}
blocks.push(
<ul key={key++} className="list-disc pl-6 space-y-1">
{items.map((item, idx) => (
<li key={idx}>{renderInline(item)}</li>
))}
</ul>
);
continue;
}
// Ordered list
if (/^\d+\.\s+/.test(line)) {
const items: string[] = [];
while (i < lines.length && /^\d+\.\s+/.test(lines[i])) {
items.push(lines[i].replace(/^\d+\.\s+/, ""));
i++;
}
blocks.push(
<ol key={key++} className="list-decimal pl-6 space-y-1">
{items.map((item, idx) => (
<li key={idx}>{renderInline(item)}</li>
))}
</ol>
);
continue;
}
// Paragraph: consume consecutive plain lines
const paragraph: string[] = [line];
i++;
while (
i < lines.length &&
lines[i].trim() !== "" &&
!/^(#{1,4})\s+/.test(lines[i]) &&
!/^[-*]\s+/.test(lines[i]) &&
!/^\d+\.\s+/.test(lines[i]) &&
!lines[i].trim().startsWith("```")
) {
paragraph.push(lines[i]);
i++;
}
blocks.push(
<p key={key++} className="leading-relaxed">
{renderInline(paragraph.join(" "))}
</p>
);
}
return <div className="space-y-3 text-sm">{blocks}</div>;
};
export default Markdown;
+2 -2
View File
@@ -5,7 +5,7 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
DropdownMenuTrigger,
} from "./components/ui/dropdown-menu"
import { useTheme } from "./theme-provider"
@@ -13,7 +13,7 @@ export function ModeToggle() {
const { setTheme } = useTheme()
return (
<DropdownMenu modal={false}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
+4 -2
View File
@@ -11,9 +11,11 @@ export default defineConfig({
{
src: '../blueprints/*',
dest: 'blueprints' // raíz de dist (public root)
},
{
src: '../meta.json',
dest: '' // raíz de dist
}
// meta.json is no longer copied from the repo root: it is
// generated into app/public/meta.json by build-scripts/generate-meta.js
]
})
Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

-44
View File
@@ -1,44 +0,0 @@
services:
9router:
image: decolua/9router:0.5.59
restart: unless-stopped
environment:
DATA_DIR: /app/data
PORT: "20128"
HOSTNAME: "0.0.0.0"
NODE_ENV: production
JWT_SECRET: ${JWT_SECRET}
INITIAL_PASSWORD: ${INITIAL_PASSWORD}
API_KEY_SECRET: ${API_KEY_SECRET}
MACHINE_ID_SALT: ${MACHINE_ID_SALT}
BASE_URL: https://${ROUTER_HOST}
NEXT_PUBLIC_BASE_URL: https://${ROUTER_HOST}
HEADROOM_URL: http://headroom:8787
AUTH_COOKIE_SECURE: "true"
REQUIRE_API_KEY: "true"
volumes:
- 9router-data:/app/data
depends_on:
headroom:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:20128/api/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
# Optional token-reduction proxy that 9Router calls through HEADROOM_URL.
# Shipped as a sidecar because the 9Router image does not bundle it.
headroom:
image: ghcr.io/chopratejas/headroom:0.6.7
restart: unless-stopped
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8787/health')"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
volumes:
9router-data:
-17
View File
@@ -1,17 +0,0 @@
{
"id": "9router",
"name": "9Router",
"version": "0.5.59",
"description": "Self-hosted AI model router that rotates requests across 40+ providers with automatic fallback, exposing an OpenAI-compatible endpoint for coding agents.",
"logo": "9router.png",
"links": {
"github": "https://github.com/decolua/9router",
"website": "https://9router.com",
"docs": "https://github.com/decolua/9router/blob/master/DOCKER.md"
},
"tags": [
"ai",
"proxy",
"developer-tools"
]
}
-21
View File
@@ -1,21 +0,0 @@
[variables]
main_domain = "${domain}"
jwt_secret = "${password:64}"
initial_password = "${password:32}"
api_key_secret = "${password:64}"
machine_id_salt = "${password:32}"
[config]
env = [
"ROUTER_HOST=${main_domain}",
"JWT_SECRET=${jwt_secret}",
"INITIAL_PASSWORD=${initial_password}",
"API_KEY_SECRET=${api_key_secret}",
"MACHINE_ID_SALT=${machine_id_salt}",
]
mounts = []
[[config.domains]]
serviceName = "9router"
port = 20128
host = "${main_domain}"
+3 -1
View File
@@ -1,7 +1,9 @@
version: "3"
services:
ackee:
image: electerious/ackee:3.4.2
expose:
ports:
- "3000"
environment:
- ACKEE_USERNAME=${ACKEE_USERNAME}
-16
View File
@@ -1,16 +0,0 @@
{
"id": "ackee",
"name": "Ackee",
"version": "latest",
"description": "Ackee is a self-hosted analytics tool for your website.",
"logo": "logo.png",
"links": {
"github": "https://github.com/electerious/Ackee",
"website": "https://ackee.electerious.com/",
"docs": "https://docs.ackee.electerious.com/"
},
"tags": [
"analytics",
"self-hosted"
]
}
+5 -3
View File
@@ -1,6 +1,8 @@
version: "3.8"
services:
activepieces:
image: activepieces/activepieces:0.91.2
image: activepieces/activepieces:0.35.0
restart: unless-stopped
depends_on:
@@ -30,7 +32,7 @@ services:
AP_TEMPLATES_SOURCE_URL: https://cloud.activepieces.com/api/v1/flow-templates
postgres:
image: postgres:17
image: postgres:14
restart: unless-stopped
environment:
@@ -59,4 +61,4 @@ services:
volumes:
postgres_data:
redis_data:
redis_data:
-17
View File
@@ -1,17 +0,0 @@
{
"id": "activepieces",
"name": "Activepieces",
"version": "0.91.2",
"description": "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.",
"logo": "activepieces.svg",
"links": {
"github": "https://github.com/activepieces/activepieces",
"website": "https://www.activepieces.com/",
"docs": "https://www.activepieces.com/docs"
},
"tags": [
"automation",
"workflow",
"no-code"
]
}
-17
View File
@@ -1,17 +0,0 @@
{
"id": "actualbudget",
"name": "Actual Budget",
"version": "latest",
"description": "A super fast and privacy-focused app for managing your finances.",
"logo": "actualbudget.png",
"links": {
"github": "https://github.com/actualbudget/actual",
"website": "https://actualbudget.org",
"docs": "https://actualbudget.org/docs"
},
"tags": [
"budgeting",
"finance",
"money"
]
}
+3 -4
View File
@@ -1,4 +1,4 @@
# dokploy: allow-host-ports — DNS (53/tcp+udp), DHCP (67/68) and DNS-over-TLS/QUIC (853) are not HTTP protocols; clients must reach them directly on the host, Traefik cannot route them.
version: "3.8"
services:
adguardhome:
image: adguard/adguardhome:latest
@@ -10,12 +10,11 @@ services:
- "68:68/tcp" # DHCP Client
- "853:853/tcp" # DNS over TLS, DNS-over-QUIC
- "853:853/udp" # DNS over TLS, DNS-over-QUIC
expose:
- 6060 # HTTP (pprof) — debug endpoint, no need to publish it on the host
- "6060:6060/tcp" # HTTP (pprof)
volumes:
- adguardhome-work:/opt/adguardhome/work
- adguardhome-conf:/opt/adguardhome/conf
volumes:
adguardhome-work: {}
adguardhome-conf: {}
adguardhome-conf: {}
-21
View File
@@ -1,21 +0,0 @@
{
"id": "adguardhome",
"name": "AdGuard Home",
"version": "latest",
"description": "AdGuard Home is a network-wide ad and tracker blocker. Self-host it on your own server with Dokploy, which deploys AdGuard Home as a Docker Compose service in a few clicks.",
"logo": "logo.svg",
"links": {
"github": "https://github.com/AdguardTeam/AdGuardHome",
"website": "https://adguard.com",
"docs": "https://github.com/AdguardTeam/AdGuardHome/wiki"
},
"tags": [
"privacy",
"security",
"dns",
"ad-blocking"
],
"seoTitle": "AdGuard Home Docker Compose Setup",
"seoDescription": "Network-wide ad and tracker blocker. Deploy AdGuard Home with Docker Compose on Dokploy in minutes – free, open source, self-hosted.",
"newConfig": true
}
+3 -2
View File
@@ -1,6 +1,7 @@
version: "3.8"
services:
adminer:
image: adminer:4.8.1
restart: unless-stopped
expose:
- 8080
ports:
- 8080
-21
View File
@@ -1,21 +0,0 @@
{
"id": "adminer",
"name": "Adminer",
"version": "4.8.1",
"description": "Adminer is a lightweight, web-based database management tool. Self-host it on your own server with Dokploy, which deploys Adminer as a Docker Compose service in a few clicks.",
"logo": "logo.svg",
"links": {
"github": "https://github.com/vrana/adminer",
"website": "https://www.adminer.org/",
"docs": "https://www.adminer.org/en/plugins/"
},
"tags": [
"databases",
"developer-tools",
"mysql",
"postgresql"
],
"seoTitle": "Adminer Docker Compose Setup",
"seoDescription": "Lightweight database management tool. Deploy Adminer with Docker Compose on Dokploy in minutes – free, open source, self-hosted.",
"newConfig": true
}
@@ -1,3 +1,5 @@
version: "3.8"
services:
web:
image: ghcr.io/seanmorley15/adventurelog-frontend:latest
-19
View File
@@ -1,19 +0,0 @@
{
"id": "adventurelog",
"name": "AdventureLog",
"version": "latest",
"description": "AdventureLog is an open-source activity tracker with maps, journaling, and Strava integration.",
"logo": "adventurelog.svg",
"links": {
"github": "https://github.com/seanmorley15/adventurelog",
"website": "https://adventurelog.app/",
"docs": "https://adventurelog.app/docs/intro/adventurelog_overview.html"
},
"tags": [
"activity",
"maps",
"django",
"react",
"postgres"
]
}
+11 -29
View File
@@ -1,8 +1,9 @@
version: "3.8"
services:
affinepro:
image: ghcr.io/toeverything/affine:stable
image: ghcr.io/toeverything/affine-graphql:stable-780dd83
restart: unless-stopped
expose:
ports:
- 3010
volumes:
- affine-storage:/root/.affine/storage
@@ -11,7 +12,6 @@ services:
- REDIS_SERVER_HOST=redis
- REDIS_SERVER_PASSWORD=${REDIS_PASSWORD}
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/affinepro
- AFFINE_INDEXER_ENABLED=false
- AFFINE_SERVER_HOST=${DOMAIN}
- MAILER_HOST=${MAILER_HOST}
- MAILER_PORT=${MAILER_PORT}
@@ -19,21 +19,16 @@ services:
- MAILER_PASSWORD=${MAILER_PASSWORD}
- MAILER_SENDER=${MAILER_SENDER}
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
migration:
condition: service_completed_successfully
- db
- redis
migration:
image: ghcr.io/toeverything/affine:stable
command: ["sh", "-c", "node ./scripts/self-host-predeploy.js"]
image: ghcr.io/toeverything/affine-graphql:stable-780dd83
command: node ./scripts/self-host-predeploy.js
environment:
- REDIS_SERVER_HOST=redis
- REDIS_SERVER_PASSWORD=${REDIS_PASSWORD}
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/affinepro
- AFFINE_INDEXER_ENABLED=false
- AFFINE_SERVER_HOST=${DOMAIN}
- MAILER_HOST=${MAILER_HOST}
- MAILER_PORT=${MAILER_PORT}
@@ -44,25 +39,17 @@ services:
- affine-storage:/root/.affine/storage
- affine-config:/root/.affine/config
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
- db
- redis
db:
image: pgvector/pgvector:pg16
image: postgres:15-alpine
restart: unless-stopped
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=affinepro
- POSTGRES_INITDB_ARGS=--data-checksums
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres", "-d", "affinepro"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
@@ -70,14 +57,9 @@ services:
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis-data:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli -a '${REDIS_PASSWORD}' ping | grep PONG"]
interval: 10s
timeout: 5s
retries: 5
volumes:
affine-storage: {}
affine-config: {}
postgres-data: {}
redis-data: {}
redis-data: {}
-20
View File
@@ -1,20 +0,0 @@
{
"id": "affinepro",
"name": "Affine Pro",
"version": "stable",
"description": "AFFiNE is a workspace for notes, docs, and whiteboards, and an alternative to Notion. Self-host it on your own server with Dokploy, which deploys AFFiNE as a Docker Compose service in a few clicks.",
"logo": "logo.png",
"links": {
"github": "https://github.com/toeverything/Affine",
"website": "https://affine.pro/",
"docs": "https://docs.affine.pro/"
},
"tags": [
"collaboration",
"self-hosted",
"productivity",
"project-management"
],
"seoTitle": "Self-Host Affine in minutes",
"seoDescription": "Workspace for notes and docs, an alternative to Notion. Self-host Affine on Dokploy in minutes – no manual setup needed."
}
-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" id="a" viewBox="0 0 3271 1440"><defs><style>.st0{fill:#dedede}</style></defs><path d="M1009.95 1209.53c-125.1-214.98-250.74-430.9-378.92-651.16-126.82 219.73-251.81 436.28-376.62 652.51H60.69c190.42-329.11 570.87-981.75 570.87-981.75s382.11 651.12 572.49 980.41h-194.1Z" class="st0"/><path d="M875.46 1210.46H383.92c32.72-56.91 64.4-111.99 95.81-166.62H781.8c30.62 54.46 61.09 108.66 93.67 166.62zm1622.92-202.23c19.52 29.84 37.9 57.94 58.46 89.39h-63.61c-18.29-24.88-38.17-51.91-57.71-78.49h-72.24v78.17h-51.11V828.44c30.03 0 58.71-.22 87.38.06 23.48.24 47.32-1.26 70.37 2.21 37.67 5.66 66.94 25.71 74.9 64.9 8.22 40.54 1.62 78.35-35.52 104.62-3.14 2.22-6.21 4.54-10.93 7.99h.01Zm-135.01-40.37c34 0 66.71 1.87 99.08-.66 21.47-1.67 30.97-17.92 31.56-39.77.61-22.88-9.01-40.56-30.99-42.73-32.8-3.23-66.15-.84-99.65-.84zM1674.39 829.9v38.37c-52.36 58.82-104.25 117.13-160.14 179.91h158.93v49.49h-227.62v-51.9c46.98-52.11 95.02-105.41 147.93-164.09h-147.28V829.9zm906.29-489.95c64.48 63.91 125.11 123.99 189.35 187.65V373.68h47.4v271.19c-64.19-62.94-125.05-122.59-190.04-186.3v162.44h-46.72V339.94h.01Zm195.02 692.31c13.79-6.85 33.51-15.76 45.41-21.65 41.44 55.26 105.07 42.53 132.33 18s35.44-59.98 25.2-95.47c-13.36-34.33-42.97-61.14-86.36-59.62-43.38 1.52-81.55 32.41-85.2 84.34h-50.32c-.57-50.11 36.27-109.35 94.12-126.75 72.77-21.89 144.89 13.23 172.07 83.77 25.18 65.37-5.74 138.6-72.21 171-60.69 29.59-140.17 6.97-175.1-53.61h.04Zm-811.82-622.07c-11.07 8.4-24.55 17.68-34.13 24.95-55.99-46.33-119.62-30.88-142.33 27.28-13.63 38.17 6.27 88.2 48.2 103.62 36.45 13.4 76.82-.61 87.32-31.48-8.66-2.61-17.29-5.23-28.25-8.54-.93-12.84-1.85-25.67-2.96-40.9h96.82c7.25 56.02-37.39 118.12-94.41 132.17-63.06 15.54-129.35-19.2-152.31-79.83-22.64-59.76 4.81-131.25 61.88-161.16 55.93-29.3 126.75-15.37 160.17 33.88Zm-531.9-55.25c48.2 92.28 93.04 178.13 139.88 267.84h-53.47c-27.89-50.55-56.47-102.34-86.73-157.13-28.93 54.29-56.1 105.29-83.82 157.31h-54.54c45.87-88.68 90.79-175.5 138.65-268.02h.01Zm1642.09 60.65h-89.98v-46.22h226.22v46.24h-86.43v205.91h-49.82V415.59zM1947.89 878.34v65.77l63.09.91s-.03 30.03-.03 50.34c-25.69.89-53.1.45-78.35-5.53-21.38-5.05-36.73-22.63-37.67-46.05-1.5-36.98-.4-75.88-.4-115.24h216.51v49.81h-163.16Zm-52.88 218.57v-68.62c14.11-.65 27.22-1.25 40.97-1.87 3.47 7 3.06 19.81 9.96 20.09 6.88.29 107.67 0 164.04 0v50.41h-214.97Zm342.4-678.53v60.83l58.36.84s-.03 27.78-.03 46.57c-23.85.41-49.12.41-72.49-5.11-19.77-4.68-33.97-20.93-34.85-42.59-1.39-34.2-.37-70.19-.37-106.6h200.29v46.07H2237.4h.01Zm-48.91 202.2v-63.49c13.05-.6 25.18-1.15 37.9-1.73 3.2 6.48 2.83 18.32 9.2 18.59s99.6 0 151.75 0v46.63h-198.87.01Z" class="st0"/></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

-13
View File
@@ -1,13 +0,0 @@
services:
agent-zero:
image: agent0ai/agent-zero:v1.9
restart: unless-stopped
expose:
- "80"
environment:
- AUTH_LOGIN=${AUTH_LOGIN}
- AUTH_PASSWORD=${AUTH_PASSWORD}
volumes:
- agent-zero-data:/a0/usr
volumes:
agent-zero-data:
-17
View File
@@ -1,17 +0,0 @@
{
"id": "agent-zero",
"name": "Agent Zero",
"version": "v1.9",
"description": "Agent Zero is an open-source autonomous AI agent framework with memory and tool use capabilities.",
"logo": "agent-zero.svg",
"links": {
"github": "https://github.com/agent0ai/agent-zero",
"website": "https://www.agent-zero.ai",
"docs": "https://www.agent-zero.ai/p/docs/"
},
"tags": [
"ai",
"agent",
"automation"
]
}
-16
View File
@@ -1,16 +0,0 @@
[variables]
main_domain = "${domain}"
auth_login = "${username}"
auth_password = "${password:32}"
[config]
env = [
"AUTH_LOGIN=${auth_login}",
"AUTH_PASSWORD=${auth_password}",
]
mounts = []
[[config.domains]]
serviceName = "agent-zero"
port = 80
host = "${main_domain}"
+3 -2
View File
@@ -11,11 +11,12 @@ services:
- agentdvr-config:/AgentDVR/Media/XML/
- agentdvr-media:/AgentDVR/Media/WebServerRoot/Media/
- agentdvr-commands:/AgentDVR/Commands/
expose:
ports:
- 8090
- 3478/udp
- 50000-50100/udp
volumes:
agentdvr-config: {}
agentdvr-media: {}
agentdvr-commands: {}
agentdvr-commands: {}
-20
View File
@@ -1,20 +0,0 @@
{
"id": "agentdvr",
"name": "Agent DVR",
"version": "latest",
"description": "Agent DVR is a comprehensive video surveillance software with motion detection, alerts, and remote access capabilities.",
"logo": "agentdvr.png",
"links": {
"github": "https://github.com/ispysoftware/AgentDVR",
"website": "https://www.ispyconnect.com/",
"docs": "https://www.ispyconnect.com/userguide-agent-dvr.aspx"
},
"tags": [
"surveillance",
"security",
"video",
"monitoring",
"dvr",
"camera"
]
}
+2 -1
View File
@@ -3,7 +3,6 @@ main_domain = "${domain}"
timezone = "America/New_York"
[config]
mounts = []
[[config.domains]]
serviceName = "agentdvr"
port = 8090
@@ -11,3 +10,5 @@ host = "${main_domain}"
[config.env]
TIMEZONE = "${timezone}"
[[config.mounts]]
-29
View File
@@ -1,29 +0,0 @@
<svg version="1.1" id="Layer_1" xmlns:x="ns_extend;" xmlns:i="ns_ai;" xmlns:graph="ns_graphs;"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 95.7 99" style="enable-background:new 0 0 95.7 99;" xml:space="preserve">
<title>Airbyte</title>
<style type="text/css">
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#615EFF;}
</style>
<metadata>
<sfw xmlns="ns_sfw;">
<slices>
</slices>
<sliceSourceBounds bottomLeftOrigin="true" height="99" width="95.7" x="77.2" y="75">
</sliceSourceBounds>
</sfw>
</metadata>
<g>
<path class="st0" d="M32.6,12C43.2,0.1,60.8-3.4,75.2,3.5c19.2,9.1,26.1,32.4,15.7,50.3L67.4,94.1c-1.3,2.3-3.5,3.9-6,4.6
c-2.5,0.7-5.2,0.3-7.5-1l28.4-48.8c7.6-13,2.5-29.9-11.4-36.5c-10.4-5-23.2-2.5-30.9,6.1c-4.2,4.7-6.6,10.8-6.7,17.1
c-0.1,6.3,2.2,12.5,6.3,17.3c0.7,0.9,1.5,1.7,2.4,2.4L25.5,83.8c-0.6,1.1-1.5,2.1-2.5,2.9c-1,0.8-2.2,1.4-3.5,1.7
c-1.3,0.3-2.6,0.4-3.8,0.3c-1.3-0.2-2.5-0.6-3.6-1.2l18-31c-2.6-3.7-4.4-7.9-5.5-12.2l-11,19c-1.3,2.3-3.5,3.9-6,4.6S2.3,68,0,66.7
l28.5-49.1C29.7,15.7,31.1,13.8,32.6,12z M65.6,27.6c6.9,3.9,9.2,12.7,5.3,19.5L43.5,94.1c-1.3,2.3-3.5,3.9-6,4.6
c-2.5,0.7-5.2,0.3-7.5-1L55.4,54c-2-0.4-4-1.3-5.6-2.5c-1.7-1.2-3.1-2.8-4-4.6c-1-1.8-1.6-3.8-1.7-5.9c-0.1-2.1,0.2-4.1,0.9-6.1
c0.7-1.9,1.9-3.7,3.4-5.1c1.5-1.4,3.3-2.5,5.2-3.2c2-0.7,4.1-0.9,6.1-0.8C61.8,26,63.8,26.6,65.6,27.6z M55.6,36.5
c-0.5,0.4-0.9,0.8-1.2,1.3h0c-0.4,0.8-0.7,1.7-0.6,2.5c0.1,0.9,0.4,1.7,0.9,2.4c0.5,0.7,1.3,1.2,2.1,1.5c0.8,0.3,1.8,0.3,2.6,0.1
c0.9-0.2,1.6-0.7,2.2-1.4c0.6-0.7,1-1.5,1.1-2.4c0.1-0.9,0-1.8-0.4-2.6c-0.4-0.8-1-1.5-1.8-1.9c-0.5-0.3-1.1-0.5-1.7-0.6
s-1.2,0-1.8,0.1C56.6,35.8,56.1,36.1,55.6,36.5z">
</path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

-428
View File
@@ -1,428 +0,0 @@
# The following template is not officially supported by the Airbyte team, as
# docker compose deployment has been superseded by abctl. It was made based
# on the community-maintained example below:
#
# - https://github.com/nktnet1/airbyte-compose
#
# NOTE: make sure to change the AIRBYTE_URL and AIRBYTE_API_HOST environment
# variables to match that of your public domain. The URL must be publicly
# accessible (for example, it cannot be auth-protected by Cloudflare).
#
# Once you've completed the setup via the web UI, you can log in using the
# password defined in the AIRBYTE_ADMIN_PASSWORD environment variable.
#
# To connect internal databases, ensure that they are on the same network as
# the k3s container. This is available in Dokploy's UI starting from v0.30.0.
name: airbyte
x-airbyte-environment: &airbyte-environment
AIRBYTE_VERSION: ${AIRBYTE_VERSION:?Set AIRBYTE_VERSION in .env}
AIRBYTE_URL: ${AIRBYTE_URL:?Set AIRBYTE_URL in .env}
AIRBYTE_API_HOST: ${AIRBYTE_API_HOST:?Set AIRBYTE_API_HOST in .env}
AIRBYTE_SERVER_HOST: server:8001
INTERNAL_API_HOST: http://server:8001
MANIFEST_SERVER_API_HOST: http://manifest-server:8080
CONTROL_PLANE_TOKEN_ENDPOINT: http://server:8001/api/v1/dataplanes/token
WORKLOAD_API_HOST: http://workload-api-server:8007
TEMPORAL_HOST: temporal:7233
MINIO_ENDPOINT: http://minio:9000
DATABASE_HOST: db
DATABASE_DB: ${DATABASE_DB:?Set DATABASE_DB in .env}
DATABASE_USER: ${DATABASE_USER:?Set DATABASE_USER in .env}
DATABASE_PASSWORD: ${DATABASE_PASSWORD:?Set DATABASE_PASSWORD in .env}
DATABASE_URL: jdbc:postgresql://db:5432/${DATABASE_DB}
CONFIG_DATABASE_REPLICA_URL: jdbc:postgresql://db:5432/${DATABASE_DB}
CONFIG_DATABASE_REPLICA_USER: ${DATABASE_USER}
CONFIG_DATABASE_REPLICA_PASSWORD: ${DATABASE_PASSWORD}
AWS_ACCESS_KEY_ID: ${MINIO_ROOT_USER:?Set MINIO_ROOT_USER in .env}
AWS_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD:?Set MINIO_ROOT_PASSWORD in .env}
AB_INSTANCE_ADMIN_PASSWORD: ${AIRBYTE_ADMIN_PASSWORD:?Set AIRBYTE_ADMIN_PASSWORD in .env}
AB_INSTANCE_ADMIN_CLIENT_ID: ${AIRBYTE_ADMIN_CLIENT_ID:?Set AIRBYTE_ADMIN_CLIENT_ID in .env}
AB_INSTANCE_ADMIN_CLIENT_SECRET: ${AIRBYTE_ADMIN_CLIENT_SECRET:?Set AIRBYTE_ADMIN_CLIENT_SECRET in .env}
AB_JWT_SIGNATURE_SECRET: ${AIRBYTE_JWT_SIGNATURE_SECRET:?Set AIRBYTE_JWT_SIGNATURE_SECRET in .env}
CONNECTOR_PROFILER_IMAGE: airbyte/async-profiler:${AIRBYTE_VERSION}
CONNECTOR_SIDECAR_IMAGE: airbyte/connector-sidecar:${AIRBYTE_VERSION}
CONTAINER_ORCHESTRATOR_IMAGE: airbyte/container-orchestrator:${AIRBYTE_VERSION}
WORKLOAD_INIT_IMAGE: airbyte/workload-init-container:${AIRBYTE_VERSION}
JAVA_OPTS: -XX:+ExitOnOutOfMemoryError -XX:MaxRAMPercentage=75.0 -XX:FlightRecorderOptions=stackdepth=256
x-airbyte-service: &airbyte-service
env_file:
- ../files/config/airbyte.env
environment: *airbyte-environment
restart: unless-stopped
services:
db:
image: airbyte/db:${AIRBYTE_VERSION}
environment:
POSTGRES_DB: ${DATABASE_DB}
POSTGRES_USER: ${DATABASE_USER}
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}
PGDATA: /var/lib/postgresql/data/pgdata
healthcheck:
test: [CMD-SHELL, 'pg_isready -U "$${POSTGRES_USER}" -d "$${POSTGRES_DB}"']
interval: 5s
timeout: 5s
retries: 30
start_period: 10s
volumes:
- db-data:/var/lib/postgresql/data
restart: unless-stopped
minio:
# S3-compatible storage (SeaweedFS). The service keeps the "minio" name so
# Airbyte's MINIO_ENDPOINT (http://minio:9000) and the k3s ExternalName
# service keep working unchanged.
image: chrislusf/seaweedfs:${SEAWEEDFS_VERSION}
entrypoint: [/bin/sh, -ec]
command:
- |
mkdir -p /etc/seaweedfs
printf '{"identities":[{"name":"airbyte","credentials":[{"accessKey":"%s","secretKey":"%s"}],"actions":["Admin","Read","Write","List","Tagging"]}]}' \
"$${MINIO_ROOT_USER}" "$${MINIO_ROOT_PASSWORD}" > /etc/seaweedfs/s3.json
exec weed server -dir=/storage -ip.bind=0.0.0.0 \
-master.volumeSizeLimitMB=1024 -volume.max=0 \
-s3 -s3.port=9000 -s3.config=/etc/seaweedfs/s3.json
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
volumes:
- minio-data:/storage
restart: unless-stopped
healthcheck:
test: [CMD-SHELL, "wget -q -O /dev/null http://127.0.0.1:9000/healthz"]
interval: 10s
timeout: 5s
retries: 30
start_period: 20s
minio-init:
image: chrislusf/seaweedfs:${SEAWEEDFS_VERSION}
entrypoint: [/bin/sh, /scripts/init-minio.sh]
environment:
SEAWEEDFS_MASTER: minio:9333
volumes:
- ../files/scripts/init-minio.sh:/scripts/init-minio.sh:ro
depends_on:
minio:
condition: service_healthy
restart: on-failure
temporal:
image: temporalio/auto-setup:${TEMPORAL_VERSION}
environment:
AUTO_SETUP: "true"
DB: postgres12
DB_PORT: "5432"
POSTGRES_SEEDS: db
POSTGRES_USER: ${DATABASE_USER}
POSTGRES_PWD: ${DATABASE_PASSWORD}
POSTGRES_TLS_ENABLED: "false"
POSTGRES_TLS_DISABLE_HOST_VERIFICATION: "false"
SQL_TLS_ENABLED: "false"
SQL_TLS_DISABLE_HOST_VERIFICATION: "false"
DYNAMIC_CONFIG_FILE_PATH: config/dynamicconfig/development.yaml
PROMETHEUS_ENDPOINT: 0.0.0.0:9090
BIND_ON_IP: 0.0.0.0
TEMPORAL_BROADCAST_ADDRESS: 0.0.0.0
volumes:
- ../files/config/temporal-dynamicconfig.yaml:/etc/temporal/config/dynamicconfig/development.yaml:ro
depends_on:
db:
condition: service_healthy
restart: unless-stopped
healthcheck:
test:
- CMD
- /bin/bash
- -ec
- exec 3<>/dev/tcp/127.0.0.1/7233
interval: 10s
timeout: 5s
retries: 30
start_period: 30s
k3s:
image: rancher/k3s:${K3S_VERSION}
command:
- server
- --node-name=airbyte-k3s
- --disable=traefik
- --disable=servicelb
- --disable=metrics-server
- --disable=coredns
- --tls-san=k3s
privileged: true
environment:
K3S_KUBECONFIG_OUTPUT: /output/kubeconfig.yaml
K3S_KUBECONFIG_MODE: "0644"
tmpfs:
- /run
- /var/run
volumes:
- k3s-data:/var/lib/rancher/k3s
- k3s-kubeconfig:/output
healthcheck:
test: [CMD, /bin/kubectl, get, --raw=/readyz]
interval: 5s
timeout: 5s
retries: 60
start_period: 20s
restart: unless-stopped
docker-dns:
image: rancher/mirrored-coredns-coredns:${COREDNS_VERSION}
command: [-conf, /etc/coredns/Corefile]
volumes:
- ../files/config/docker-dns.Corefile:/etc/coredns/Corefile:ro
network_mode: service:k3s
depends_on:
k3s:
condition: service_started
restart: unless-stopped
healthcheck:
test: [CMD, /coredns, -version]
interval: 10s
timeout: 5s
retries: 3
k3s-bootstrap:
image: rancher/k3s:${K3S_VERSION}
entrypoint: [/bin/sh, /scripts/bootstrap-k3s.sh]
environment:
AIRBYTE_ADMIN_PASSWORD: ${AIRBYTE_ADMIN_PASSWORD}
AIRBYTE_ADMIN_CLIENT_ID: ${AIRBYTE_ADMIN_CLIENT_ID}
AIRBYTE_ADMIN_CLIENT_SECRET: ${AIRBYTE_ADMIN_CLIENT_SECRET}
AIRBYTE_JWT_SIGNATURE_SECRET: ${AIRBYTE_JWT_SIGNATURE_SECRET}
DATABASE_USER: ${DATABASE_USER}
DATABASE_PASSWORD: ${DATABASE_PASSWORD}
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
volumes:
- k3s-kubeconfig:/kube
- ../files/config/k3s-resources.yaml:/config/k3s-resources.yaml:ro
- ../files/scripts/bootstrap-k3s.sh:/scripts/bootstrap-k3s.sh:ro
depends_on:
k3s:
condition: service_healthy
docker-dns:
condition: service_started
restart: on-failure
dataplane-credentials:
image: rancher/k3s:${K3S_VERSION}
entrypoint: [/bin/sh, /scripts/export-dataplane-credentials.sh]
volumes:
- k3s-kubeconfig:/kube:ro
- dataplane-credentials:/credentials
- ../files/scripts/export-dataplane-credentials.sh:/scripts/export-dataplane-credentials.sh:ro
depends_on:
bootloader:
condition: service_completed_successfully
restart: on-failure
bootloader:
<<: *airbyte-service
image: airbyte/bootloader:${AIRBYTE_VERSION}
environment:
<<: *airbyte-environment
KUBECONFIG: /kube/config.yaml
MICRONAUT_ENVIRONMENTS: k8s,control-plane,edition-community
JOB_KUBE_NAMESPACE: default
DD_SERVICE: airbyte-bootloader
volumes:
- k3s-kubeconfig:/kube:ro
depends_on:
db:
condition: service_healthy
k3s-bootstrap:
condition: service_completed_successfully
restart: on-failure
server:
<<: *airbyte-service
image: airbyte/server:${AIRBYTE_VERSION}
environment:
<<: *airbyte-environment
DD_SERVICE: airbyte-server
expose:
- 8001
healthcheck:
test:
- CMD
- /bin/bash
- -ec
- |
exec 3<>/dev/tcp/127.0.0.1/8001
printf 'GET /api/v1/health HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3
read -r status <&3
[[ "$$status" == *" 200 "* ]]
interval: 5s
timeout: 10s
retries: 60
start_period: 10s
depends_on:
bootloader:
condition: service_completed_successfully
minio-init:
condition: service_completed_successfully
temporal:
condition: service_started
manifest-server:
<<: *airbyte-service
image: airbyte/manifest-server:${MANIFEST_SERVER_VERSION}
environment:
<<: *airbyte-environment
DD_SERVICE: airbyte-manifest-server
AIRBYTE_ENABLE_UNSAFE_CODE: ${AIRBYTE_ENABLE_UNSAFE_CODE:-false}
depends_on:
bootloader:
condition: service_completed_successfully
minio-init:
condition: service_completed_successfully
healthcheck:
test:
- CMD
- python
- -c
- import urllib.request; response = urllib.request.urlopen("http://127.0.0.1:8080/health/", timeout=5); assert response.status == 200
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
workload-api-server:
<<: *airbyte-service
image: airbyte/workload-api-server:${AIRBYTE_VERSION}
environment:
<<: *airbyte-environment
DD_SERVICE: airbyte-workload-api
depends_on:
bootloader:
condition: service_completed_successfully
minio-init:
condition: service_completed_successfully
temporal:
condition: service_started
healthcheck:
test:
- CMD
- /bin/bash
- -ec
- |
exec 3<>/dev/tcp/127.0.0.1/8085
printf 'GET /health/liveness HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3
read -r status <&3
[[ "$$status" == *" 200 "* ]]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
worker:
<<: *airbyte-service
image: airbyte/worker:${AIRBYTE_VERSION}
environment:
<<: *airbyte-environment
DD_SERVICE: airbyte-worker
depends_on:
bootloader:
condition: service_completed_successfully
temporal:
condition: service_started
workload-api-server:
condition: service_started
healthcheck:
test:
- CMD
- /bin/bash
- -ec
- |
exec 3<>/dev/tcp/127.0.0.1/9000
printf 'GET / HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3
read -r status <&3
[[ "$$status" == *" 200 "* ]]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
cron:
<<: *airbyte-service
image: airbyte/cron:${AIRBYTE_VERSION}
environment:
<<: *airbyte-environment
DD_SERVICE: airbyte-cron
depends_on:
bootloader:
condition: service_completed_successfully
temporal:
condition: service_started
workload-api-server:
condition: service_started
healthcheck:
test:
- CMD
- /bin/bash
- -ec
- |
exec 3<>/dev/tcp/127.0.0.1/8085
printf 'GET /health HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3
read -r status <&3
[[ "$$status" == *" 200 "* ]]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
workload-launcher:
<<: *airbyte-service
image: airbyte/workload-launcher:${AIRBYTE_VERSION}
entrypoint: [/bin/sh, /scripts/start-workload-launcher.sh]
command: [/bin/bash, -c, 'airbyte-app/bin/$${APPLICATION}']
environment:
<<: *airbyte-environment
KUBECONFIG: /kube/config.yaml
MICRONAUT_ENVIRONMENTS: k8s,control-plane,edition-community
JOB_KUBE_NAMESPACE: default
DD_SERVICE: airbyte-workload-launcher
AIRBYTE_ENABLE_UNSAFE_CODE: ${AIRBYTE_ENABLE_UNSAFE_CODE:-false}
volumes:
- k3s-kubeconfig:/kube:ro
- dataplane-credentials:/credentials:ro
- ../files/scripts/start-workload-launcher.sh:/scripts/start-workload-launcher.sh:ro
depends_on:
dataplane-credentials:
condition: service_completed_successfully
k3s-bootstrap:
condition: service_completed_successfully
server:
condition: service_healthy
workload-api-server:
condition: service_started
temporal:
condition: service_started
healthcheck:
test:
- CMD
- /bin/bash
- -ec
- |
exec 3<>/dev/tcp/127.0.0.1/8085
printf 'GET /health/readiness HTTP/1.0\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3
read -r status <&3
[[ "$$status" == *" 200 "* ]]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
volumes:
db-data:
minio-data:
k3s-data:
k3s-kubeconfig:
dataplane-credentials:
-18
View File
@@ -1,18 +0,0 @@
{
"id": "airbyte",
"name": "Airbyte",
"version": "2.2.0",
"description": "Open-source data movement for ELT pipelines and AI agents.",
"logo": "airbyte.svg",
"links": {
"github": "https://github.com/airbytehq/airbyte",
"website": "https://airbyte.com",
"docs": "https://docs.airbyte.com"
},
"tags": [
"analytics",
"data",
"elt",
"pipeline"
]
}
-656
View File
@@ -1,656 +0,0 @@
[variables]
main_domain = "${domain}"
database_password = "${password:32}"
minio_root_password = "${password:32}"
airbyte_admin_password = "${password:32}"
airbyte_admin_client_id = "${password:32}"
airbyte_admin_client_secret = "${password:32}"
airbyte_jwt_signature_secret = "${password:32}"
[[config.domains]]
serviceName = "server"
port = 8_001
host = "${main_domain}"
[config.env]
AIRBYTE_URL = "http://${main_domain}"
AIRBYTE_API_HOST = "http://${main_domain}/api/public"
AIRBYTE_VERSION = "2.2.0"
MANIFEST_SERVER_VERSION = "7.23.7"
TEMPORAL_VERSION = "1.27.2"
SEAWEEDFS_VERSION = "4.48"
K3S_VERSION = "v1.36.3-k3s1"
COREDNS_VERSION = "1.14.7"
DATABASE_DB = "db-airbyte"
DATABASE_USER = "airbyte"
DATABASE_PASSWORD = "${database_password}"
MINIO_ROOT_USER = "minio"
MINIO_ROOT_PASSWORD = "${minio_root_password}"
AIRBYTE_ADMIN_PASSWORD = "${airbyte_admin_password}"
AIRBYTE_ADMIN_CLIENT_ID = "${airbyte_admin_client_id}"
AIRBYTE_ADMIN_CLIENT_SECRET = "${airbyte_admin_client_secret}"
AIRBYTE_JWT_SIGNATURE_SECRET = "${airbyte_jwt_signature_secret}"
AIRBYTE_ENABLE_UNSAFE_CODE = "false"
[[config.mounts]]
filePath = "./config/airbyte.env"
content = """
AB_AUTH_SECRET_CREATION_ENABLED=true
AB_KUBERNETES_SECRET_NAME=airbyte-auth-secrets
AB_INSTANCE_ADMIN_PASSWORD_SECRET_KEY=instance-admin-password
AB_INSTANCE_ADMIN_CLIENT_ID_SECRET_KEY=instance-admin-client-id
AB_INSTANCE_ADMIN_CLIENT_SECRET_SECRET_KEY=instance-admin-client-secret
AB_JWT_SIGNATURE_SECRET_KEY=jwt-signature-secret
DATAPLANE_CLIENT_ID_SECRET_NAME=airbyte-auth-secrets
DATAPLANE_CLIENT_ID_SECRET_KEY=dataplane-client-id
DATAPLANE_CLIENT_SECRET_SECRET_NAME=airbyte-auth-secrets
DATAPLANE_CLIENT_SECRET_SECRET_KEY=dataplane-client-secret
IDENTITY_PROVIDER_TYPE=simple
AB_AIRBYTE_AUTH_IDENTITY_PROVIDER_VERIFY_ISSUER=false
AB_AIRBYTE_AUTH_IDENTITY_PROVIDER_VERIFY_AUDIENCE=false
AB_COOKIE_SECURE=false
AB_COOKIE_SAME_SITE=Lax
AIRBYTE_EDITION=community
AIRBYTE_CLUSTER_TYPE=hybrid
CLUSTER_TYPE=hybrid
API_AUTHORIZATION_ENABLED=true
LOCAL=false
DEPLOYMENT_ENV=docker-compose
AIRBYTE_DUMMY_SECRET=dummy
CONNECTOR_REGISTRY_SEED_PROVIDER=remote
ENTERPRISE_SOURCE_STUBS_URL=https://connectors.airbyte.com/files/resources/connector_stubs/v1/connector_stubs.json
UPDATE_DEFINITIONS_CRON_ENABLED=true
RUN_DECLARATIVE_SOURCES_UPDATER=false
CONNECTOR_ROLLOUT_EXPIRATION_SECONDS=604800
CONNECTOR_ROLLOUT_WAIT_BETWEEN_ROLLOUTS_SECONDS=10800
CONNECTOR_ROLLOUT_WAIT_BETWEEN_SYNC_RESULTS_QUERIES_SECONDS=3600
DATABASE_PORT=5432
RUN_DATABASE_MIGRATION_ON_STARTUP=true
CONFIGS_DATABASE_MINIMUM_FLYWAY_MIGRATION_VERSION=0.35.15.001
JOBS_DATABASE_MINIMUM_FLYWAY_MIGRATION_VERSION=0.29.15.001
FEATURE_FLAG_CLIENT=configfile
FEATURE_FLAG_PATH=/etc/launchdarkly/flags.yml
JOB_KUBE_SERVICEACCOUNT=airbyte-admin
JOB_KUBE_NAMESPACE=default
JOB_KUBE_LOCAL_VOLUME_ENABLED=false
JOB_ERROR_REPORTING_STRATEGY=logging
LOG_LEVEL=info
MICROMETER_METRICS_ENABLED=false
MICROMETER_METRICS_STEP=PT1M
MICROMETER_METRICS_STATSD_FLAVOR=datadog
MICROMETER_METRICS_OTLP_ENABLED=false
MICROMETER_METRICS_STATSD_ENABLED=false
PUBLISH_METRICS=true
STATSD_HOST=127.0.0.1
STATSD_PORT=8125
OTEL_SDK_DISABLED=true
DD_ENABLED=false
DD_INTEGRATIONS_ENABLED=false
DD_PROFILING_ENABLED=false
STIGG_ENABLED=false
AUDIT_LOGGING_ENABLED=false
STORAGE_TYPE=minio
STORAGE_BUCKET_ACTIVITY_PAYLOAD=airbyte-bucket
STORAGE_BUCKET_AUDIT_LOGGING=airbyte-bucket
STORAGE_BUCKET_LOG=airbyte-bucket
STORAGE_BUCKET_STATE=airbyte-bucket
STORAGE_BUCKET_WORKLOAD_OUTPUT=airbyte-bucket
S3_PATH_STYLE_ACCESS=true
AUTO_SETUP=true
DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development.yaml
PROMETHEUS_ENDPOINT=0.0.0.0:9090
BIND_ON_IP=0.0.0.0
TEMPORAL_BROADCAST_ADDRESS=0.0.0.0
TEMPORAL_CLOUD_ENABLED=false
TEMPORAL_SDK_RPC_TIMEOUT=120s
TEMPORAL_SDK_RPC_LONG_POLL_TIMEOUT=140s
TEMPORAL_SDK_RPC_QUERY_TIMEOUT=20s
TEMPORAL_WORKER_PORTS=9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 9014 9015 9016 9017 9018 9019 9020 9021 9022 9023 9024 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040
TRACKING_ENABLED=true
TRACKING_STRATEGY=segment
SEGMENT_WRITE_KEY=7UDdp5K55CyiGgsauOr2pNNujGvmhaeu
API_URL=/api/v1
CONFIG_ROOT=/configs
MAX_CHECK_WORKERS=30
MAX_NOTIFY_WORKERS=30
MAX_SYNC_WORKERS=5
SYNC_JOB_MAX_ATTEMPTS=3
SYNC_JOB_MAX_TIMEOUT_DAYS=3
SYNC_JOB_INIT_RETRY_TIMEOUT_MINUTES=5
USE_CUSTOM_NODE_SELECTOR=false
USE_STREAM_CAPABLE_STATE=true
WORKSPACE_DOCKER_MOUNT=airbyte_workspace
WORKSPACE_ROOT=/workspace
WORKER_ENVIRONMENT=kubernetes
WORKLOAD_API_SERVER_ENABLED=true
WORKLOAD_LAUNCHER_ENABLED=true
WORKLOAD_LAUNCHER_PARALLELISM=10
CONTAINER_ORCHESTRATOR_ENABLED=true
KUBERNETES_CLIENT_MAX_IDLE_CONNECTIONS=100
PUB_SUB_ENABLED=false
DATA_CHECK_TASK_QUEUES=CHECK_CONNECTION
DATA_DISCOVER_TASK_QUEUES=DISCOVER_SCHEMA
DATA_SYNC_TASK_QUEUES=SYNC
CONNECTOR_SPECIFIC_RESOURCE_DEFAULTS_ENABLED=true
FILE_TRANSFER_EPHEMERAL_STORAGE_LIMIT=5G
FILE_TRANSFER_EPHEMERAL_STORAGE_REQUEST=5G
MICRONAUT_ENVIRONMENTS=k8s,control-plane,edition-community
LAUNCHER_MICRONAUT_ENVIRONMENTS=control-plane,oss
WORKERS_MICRONAUT_ENVIRONMENTS=control-plane
CRON_MICRONAUT_ENVIRONMENTS=control-plane
SERVER_MICRONAUT_ENVIRONMENTS=control-plane
"""
[[config.mounts]]
filePath = "./config/docker-dns.Corefile"
content = """
.:53 {
bind eth0
errors
cache 30
forward . 127.0.0.11
}
"""
[[config.mounts]]
filePath = "./config/k3s-resources.yaml"
content = """
apiVersion: v1
kind: ServiceAccount
metadata:
name: airbyte-admin
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: airbyte-admin-role
namespace: default
rules:
- apiGroups: ["*"]
resources:
- configmaps
- endpoints
- jobs
- pods
- pods/log
- pods/exec
- pods/attach
- secrets
verbs: [get, list, watch, create, update, patch, delete]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: airbyte-admin-binding
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: airbyte-admin-role
subjects:
- kind: ServiceAccount
name: airbyte-admin
namespace: default
---
apiVersion: v1
kind: Service
metadata:
name: airbyte-db-svc
namespace: default
spec:
type: ExternalName
externalName: db
ports:
- name: postgresql
port: 5432
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: airbyte-minio-svc
namespace: default
spec:
type: ExternalName
externalName: minio
ports:
- name: api
port: 9000
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: airbyte-temporal
namespace: default
spec:
type: ExternalName
externalName: temporal
ports:
- name: grpc
port: 7233
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: airbyte-airbyte-server-svc
namespace: default
spec:
type: ExternalName
externalName: server
ports:
- name: http
port: 8001
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: airbyte-workload-api-server-svc
namespace: default
spec:
type: ExternalName
externalName: workload-api-server
ports:
- name: http
port: 8007
protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
name: airbyte-airbyte-manifest-server-svc
namespace: default
spec:
type: ExternalName
externalName: manifest-server
ports:
- name: http
port: 8080
protocol: TCP
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: coredns
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: system:coredns
rules:
- apiGroups: [""]
resources: [endpoints, services, pods, namespaces]
verbs: [list, watch]
- apiGroups: [discovery.k8s.io]
resources: [endpointslices]
verbs: [list, watch]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:coredns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:coredns
subjects:
- kind: ServiceAccount
name: coredns
namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough
}
cache 30
loop
reload
loadbalance
forward . 127.0.0.1:1053
}
.:1053 {
bind 127.0.0.1
errors
rewrite stop name regex ^(.+?)(\\.[^.]+\\.svc\\.cluster\\.local|\\.svc\\.cluster\\.local|\\.cluster\\.local)\\.$ {1} answer auto
forward . {$AIRBYTE_K3S_IP}
}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
labels:
k8s-app: kube-dns
spec:
replicas: 1
selector:
matchLabels:
k8s-app: kube-dns
template:
metadata:
labels:
k8s-app: kube-dns
spec:
priorityClassName: system-cluster-critical
serviceAccountName: coredns
nodeSelector:
kubernetes.io/os: linux
tolerations:
- key: CriticalAddonsOnly
operator: Exists
- key: node-role.kubernetes.io/control-plane
operator: Exists
- key: node-role.kubernetes.io/master
operator: Exists
effect: NoSchedule
containers:
- name: coredns
image: rancher/mirrored-coredns-coredns:1.14.7
imagePullPolicy: IfNotPresent
args: [-conf, /etc/coredns/Corefile]
env:
- name: AIRBYTE_K3S_IP
valueFrom:
fieldRef:
fieldPath: status.hostIP
resources:
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
ports:
- name: dns
containerPort: 53
protocol: UDP
- name: dns-tcp
containerPort: 53
protocol: TCP
volumeMounts:
- name: config
mountPath: /etc/coredns
readOnly: true
securityContext:
allowPrivilegeEscalation: false
capabilities:
add: [NET_BIND_SERVICE]
drop: [ALL]
readOnlyRootFilesystem: true
livenessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8181
periodSeconds: 2
dnsPolicy: Default
volumes:
- name: config
configMap:
name: coredns
---
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: CoreDNS
spec:
selector:
k8s-app: kube-dns
clusterIP: 10.43.0.10
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
"""
[[config.mounts]]
filePath = "./config/temporal-dynamicconfig.yaml"
content = """
frontend.enableClientVersionCheck:
- value: true
constraints: {}
history.persistenceMaxQPS:
- value: 3000
constraints: {}
frontend.persistenceMaxQPS:
- value: 3000
constraints: {}
frontend.historyMgrNumConns:
- value: 30
constraints: {}
frontend.throttledLogRPS:
- value: 20
constraints: {}
frontend.enableUpdateWorkflowExecution:
- value: true
frontend.enableUpdateWorkflowExecutionAsyncAccepted:
- value: true
history.historyMgrNumConns:
- value: 50
constraints: {}
system.advancedVisibilityWritingMode:
- value: "off"
constraints: {}
history.defaultActivityRetryPolicy:
- value:
InitialIntervalInSeconds: 1
MaximumIntervalCoefficient: 100.0
BackoffCoefficient: 2.0
MaximumAttempts: 0
history.defaultWorkflowRetryPolicy:
- value:
InitialIntervalInSeconds: 1
MaximumIntervalCoefficient: 100.0
BackoffCoefficient: 2.0
MaximumAttempts: 0
limit.blobSize.error:
- value: 15728640
constraints: {}
limit.blobSize.warn:
- value: 10485760
constraints: {}
"""
[[config.mounts]]
filePath = "./scripts/bootstrap-k3s.sh"
content = """
#!/bin/sh
set -eu
source_config=/kube/kubeconfig.yaml
client_config=/kube/config.yaml
until [ -s "$source_config" ]; do
sleep 1
done
sed 's|server: https://127.0.0.1:6443|server: https://k3s:6443|' \
"$source_config" > "$client_config"
chmod 0644 "$client_config"
/bin/kubectl --kubeconfig "$client_config" \
config set-context --current --namespace=default >/dev/null
until /bin/kubectl --kubeconfig "$client_config" get --raw=/readyz >/dev/null 2>&1; do
sleep 2
done
/bin/kubectl --kubeconfig "$client_config" apply -f /config/k3s-resources.yaml
/bin/kubectl --kubeconfig "$client_config" \
--namespace kube-system rollout status deployment/coredns --timeout=180s
/bin/kubectl --kubeconfig "$client_config" create secret generic airbyte-auth-secrets \
--namespace default \
--from-literal=instance-admin-password="$AIRBYTE_ADMIN_PASSWORD" \
--from-literal=instance-admin-client-id="$AIRBYTE_ADMIN_CLIENT_ID" \
--from-literal=instance-admin-client-secret="$AIRBYTE_ADMIN_CLIENT_SECRET" \
--from-literal=jwt-signature-secret="$AIRBYTE_JWT_SIGNATURE_SECRET" \
--from-literal=dataplane-client-id= \
--from-literal=dataplane-client-secret= \
--dry-run=client -o yaml | \
/bin/kubectl --kubeconfig "$client_config" apply -f -
/bin/kubectl --kubeconfig "$client_config" create secret generic airbyte-airbyte-secrets \
--namespace default \
--from-literal=AIRBYTE_DUMMY_SECRET=dummy \
--from-literal=AB_INSTANCE_ADMIN_PASSWORD="$AIRBYTE_ADMIN_PASSWORD" \
--from-literal=AB_INSTANCE_ADMIN_CLIENT_ID="$AIRBYTE_ADMIN_CLIENT_ID" \
--from-literal=AB_INSTANCE_ADMIN_CLIENT_SECRET="$AIRBYTE_ADMIN_CLIENT_SECRET" \
--from-literal=AB_JWT_SIGNATURE_SECRET="$AIRBYTE_JWT_SIGNATURE_SECRET" \
--from-literal=DATABASE_USER="$DATABASE_USER" \
--from-literal=DATABASE_PASSWORD="$DATABASE_PASSWORD" \
--from-literal=CONFIG_DATABASE_REPLICA_USER="$DATABASE_USER" \
--from-literal=CONFIG_DATABASE_REPLICA_PASSWORD="$DATABASE_PASSWORD" \
--from-literal=AWS_ACCESS_KEY_ID="$MINIO_ROOT_USER" \
--from-literal=AWS_SECRET_ACCESS_KEY="$MINIO_ROOT_PASSWORD" \
--from-literal=DATAPLANE_CLIENT_ID= \
--from-literal=DATAPLANE_CLIENT_SECRET= \
--dry-run=client -o yaml | \
/bin/kubectl --kubeconfig "$client_config" apply -f -
"""
[[config.mounts]]
filePath = "./scripts/export-dataplane-credentials.sh"
content = """
#!/bin/sh
set -eu
kubeconfig=/kube/config.yaml
namespace=default
secret_name=airbyte-auth-secrets
credentials_dir=/credentials
client_id_file="$credentials_dir/dataplane-client-id"
client_secret_file="$credentials_dir/dataplane-client-secret"
client_id_tmp="$credentials_dir/.dataplane-client-id.$$"
client_secret_tmp="$credentials_dir/.dataplane-client-secret.$$"
umask 022
mkdir -p "$credentials_dir"
trap 'rm -f "$client_id_tmp" "$client_secret_tmp"' 0 1 2 15
client_id_b64=$(
/bin/kubectl --kubeconfig "$kubeconfig" \
--namespace "$namespace" \
get secret "$secret_name" \
-o 'jsonpath={.data.dataplane-client-id}'
)
client_secret_b64=$(
/bin/kubectl --kubeconfig "$kubeconfig" \
--namespace "$namespace" \
get secret "$secret_name" \
-o 'jsonpath={.data.dataplane-client-secret}'
)
[ -n "$client_id_b64" ] || {
echo "The bootloader did not create a data-plane client ID." >&2
exit 1
}
[ -n "$client_secret_b64" ] || {
echo "The bootloader did not create a data-plane client secret." >&2
exit 1
}
printf '%s' "$client_id_b64" | base64 -d > "$client_id_tmp"
printf '%s' "$client_secret_b64" | base64 -d > "$client_secret_tmp"
client_id=$(cat "$client_id_tmp")
if ! printf '%s' "$client_id" | grep -Eq \
'^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$'; then
echo "The bootloader returned an invalid data-plane client ID." >&2
exit 1
fi
mv -f "$client_id_tmp" "$client_id_file"
mv -f "$client_secret_tmp" "$client_secret_file"
trap - 0 1 2 15
"""
[[config.mounts]]
filePath = "./scripts/init-minio.sh"
content = """
#!/bin/sh
set -eu
for bucket in airbyte-bucket state-storage airbyte-dev-logs; do
until echo "s3.bucket.list" | weed shell -master="$SEAWEEDFS_MASTER" 2>/dev/null | grep -q "^[[:space:]]*$bucket[[:space:]]"; do
echo "s3.bucket.create -name $bucket" | weed shell -master="$SEAWEEDFS_MASTER" >/dev/null 2>&1 || true
sleep 2
done
echo "bucket $bucket ready"
done
"""
[[config.mounts]]
filePath = "./scripts/start-workload-launcher.sh"
content = """
#!/bin/sh
set -eu
client_id_file=/credentials/dataplane-client-id
client_secret_file=/credentials/dataplane-client-secret
[ -s "$client_id_file" ] || {
echo "Missing generated data-plane client ID." >&2
exit 1
}
[ -s "$client_secret_file" ] || {
echo "Missing generated data-plane client secret." >&2
exit 1
}
DATAPLANE_CLIENT_ID=$(cat "$client_id_file")
DATAPLANE_CLIENT_SECRET=$(cat "$client_secret_file")
export DATAPLANE_CLIENT_ID DATAPLANE_CLIENT_SECRET
exec /entrypoint.sh "$@"
"""
+2
View File
@@ -1,3 +1,5 @@
version: "3.8"
services:
akaunting:
image: docker.io/akaunting/akaunting:latest
-19
View File
@@ -1,19 +0,0 @@
{
"id": "akaunting",
"name": "Akaunting",
"version": "latest",
"description": "Akaunting is a self-hosted, open-source accounting app for small businesses.",
"logo": "image.png",
"links": {
"github": "https://github.com/akaunting/akaunting",
"website": "https://akaunting.com",
"docs": "https://akaunting.com/docs"
},
"tags": [
"finance",
"accounting",
"php",
"mariadb",
"self-hosted"
]
}
+2 -2
View File
@@ -24,7 +24,6 @@ port = 80
host = "${main_domain}"
[config]
mounts = []
env = [
# App
"APP_URL=${main_domain}",
@@ -51,4 +50,5 @@ env = [
"MYSQL_RANDOM_ROOT_PASSWORD=yes"
]
# No custom mounts needed; volumes are already defined in compose.
# No custom mounts needed; volumes are already defined in compose.
[[config.mounts]]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

-30
View File
@@ -1,30 +0,0 @@
services:
alarik:
image: ghcr.io/achtungsoftware/alarik:latest
restart: unless-stopped
volumes:
- alarik-storage:/app/Storage
environment:
- API_BASE_URL=${API_BASE_URL}
- CONSOLE_BASE_URL=${CONSOLE_BASE_URL}
- ADMIN_USERNAME=${ADMIN_USERNAME}
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
- JWT=${JWT}
- ALLOW_ACCOUNT_CREATION=${ALLOW_ACCOUNT_CREATION}
expose:
- 8080
console:
image: ghcr.io/achtungsoftware/alarik-console:latest
restart: unless-stopped
environment:
- NUXT_PUBLIC_API_BASE_URL=${API_BASE_URL}
- NUXT_PUBLIC_CONSOLE_BASE_URL=${CONSOLE_BASE_URL}
- NUXT_PUBLIC_ALLOW_ACCOUNT_CREATION=${ALLOW_ACCOUNT_CREATION}
expose:
- 3000
depends_on:
- alarik
volumes:
alarik-storage:
-17
View File
@@ -1,17 +0,0 @@
{
"id": "alarik",
"name": "Alarik",
"description": "Alarik is a high-performance, S3-compatible object storage.",
"logo": "alarik.png",
"version": "latest",
"links": {
"github": "https://github.com/achtungsoftware/alarik",
"website": "https://alarik.io/",
"docs": "https://alarik.io/docs"
},
"tags": [
"storage",
"s3",
"object-storage"
]
}
-26
View File
@@ -1,26 +0,0 @@
[variables]
console_domain = "${domain}"
api_domain = "${domain}"
[config]
mounts = []
# S3-compatible API - point AWS CLI / SDKs / rclone / backup tools at this domain
[[config.domains]]
serviceName = "alarik"
port = 8_080
host = "${api_domain}"
# Web management console - log in with the generated admin credentials below
[[config.domains]]
serviceName = "console"
port = 3_000
host = "${console_domain}"
[config.env]
API_BASE_URL = "http://${api_domain}"
CONSOLE_BASE_URL = "http://${console_domain}"
ADMIN_USERNAME = "alarik"
ADMIN_PASSWORD = "${password:16}"
JWT = "${base64:64}"
ALLOW_ACCOUNT_CREATION = "false"
+3 -2
View File
@@ -1,6 +1,7 @@
version: '3.3'
services:
alist:
image: xhofe/alist:v3.55.0
image: xhofe/alist:v3.41.0
volumes:
- alist-data:/opt/alist/data
environment:
@@ -10,4 +11,4 @@ services:
restart: unless-stopped
volumes:
alist-data:
alist-data:
-17
View File
@@ -1,17 +0,0 @@
{
"id": "alist",
"name": "AList",
"version": "v3.55.0",
"description": "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.",
"logo": "alist.svg",
"links": {
"github": "https://github.com/AlistGo/alist",
"website": "https://alistgo.com/",
"docs": "https://alistgo.com/guide/install/docker.html"
},
"tags": [
"file",
"webdav",
"storage"
]
}
+3 -2
View File
@@ -1,11 +1,12 @@
version: "3.8"
services:
alltube:
image: dnomd343/alltube:latest
restart: unless-stopped
expose:
ports:
- 80
environment:
- TITLE=${TITLE}
- CONVERT=${CONVERT}
- STREAM=${STREAM}
- REMUX=${REMUX}
- REMUX=${REMUX}
-17
View File
@@ -1,17 +0,0 @@
{
"id": "alltube",
"name": "AllTube",
"version": "latest",
"description": "AllTube Download is an application designed to facilitate the downloading of videos from YouTube and other video sites. It provides an HTML GUI for youtube-dl with video conversion capabilities and JSON API support.",
"logo": "logo.png",
"links": {
"github": "https://github.com/Rudloff/alltube",
"website": "https://github.com/Rudloff/alltube",
"docs": "https://github.com/Rudloff/alltube/wiki"
},
"tags": [
"media",
"video",
"downloader"
]
}
+3 -2
View File
@@ -1,8 +1,9 @@
version: "3.8"
services:
ampache:
image: ampache/ampache:latest
restart: unless-stopped
expose:
ports:
- 80
volumes:
- config:/var/www/config
@@ -13,4 +14,4 @@ services:
volumes:
config: {}
log: {}
mysql: {}
mysql: {}
-17
View File
@@ -1,17 +0,0 @@
{
"id": "ampache",
"name": "Ampache",
"version": "latest",
"description": "Ampache is a web-based audio/video streaming application and file manager allowing you to access your music & videos from anywhere, using almost any internet enabled device.",
"logo": "logo.png",
"links": {
"github": "https://github.com/ampache/ampache",
"website": "http://ampache.org/",
"docs": "https://github.com/ampache/ampache/wiki"
},
"tags": [
"media",
"music",
"streaming"
]
}
+3 -2
View File
@@ -1,8 +1,9 @@
version: "3.8"
services:
anonupload:
image: ghcr.io/supernova3339/anonfiles:1
restart: unless-stopped
expose:
ports:
- 80
environment:
- ADMIN_EMAIL=${ADMIN_EMAIL}
@@ -12,4 +13,4 @@ services:
- uploads:/var/www/html/uploads
volumes:
uploads: {}
uploads: {}
-16
View File
@@ -1,16 +0,0 @@
{
"id": "anonupload",
"name": "AnonUpload",
"version": "1",
"description": "AnonUpload is a secure, anonymous file sharing application that does not require a database. It is built with privacy as a priority, ensuring that the direct filename used is not displayed.",
"logo": "logo.png",
"links": {
"github": "https://github.com/supernova3339/anonupload",
"docs": "https://github.com/Supernova3339/anonupload/blob/main/env.md",
"website": "https://anonupload.com/"
},
"tags": [
"file-sharing",
"privacy"
]
}
+2
View File
@@ -1,3 +1,5 @@
version: "3.8"
services:
anse-demo:
image: ddiu8081/anse:latest
-18
View File
@@ -1,18 +0,0 @@
{
"id": "anse",
"name": "Anse",
"version": "latest",
"description": "Anse is an open-source alternative to ChatGPT web UI, supporting OpenAI-compatible APIs.",
"logo": "image.png",
"links": {
"github": "https://github.com/ddiu8081/anse",
"website": "https://anse.app/",
"docs": "https://github.com/ddiu8081/anse#readme"
},
"tags": [
"ai",
"chatbot",
"openai",
"ui"
]
}
+1 -1
View File
@@ -1,7 +1,7 @@
services:
answer:
image: apache/answer:1.4.1
expose:
ports:
- '80'
restart: on-failure
volumes:
-16
View File
@@ -1,16 +0,0 @@
{
"id": "answer",
"name": "Answer",
"version": "v1.4.1",
"description": "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.",
"logo": "answer.png",
"links": {
"github": "https://github.com/apache/answer",
"website": "https://answer.apache.org/",
"docs": "https://answer.apache.org/docs"
},
"tags": [
"q-and-a",
"self-hosted"
]
}
+2 -1
View File
@@ -1,8 +1,9 @@
version: "3.8"
services:
anubis:
image: ghcr.io/techarohq/anubis:latest
restart: unless-stopped
expose:
ports:
- "8923" # Anubis default port
environment:
# Required: Point to your frontend service locally
-16
View File
@@ -1,16 +0,0 @@
{
"id": "anubis",
"name": "Anubis",
"version": "latest",
"description": "Anubis is a bot protector, It will block bots from accessing your website.",
"logo": "anubis.webp",
"links": {
"github": "https://github.com/TecharoHQ/anubis",
"website": "https://anubis.techaro.lol",
"docs": "https://anubis.techaro.lol/docs/"
},
"tags": [
"self-hosted",
"bot-protection"
]
}
+3 -2
View File
@@ -1,8 +1,9 @@
version: "3.8"
services:
anythingllm:
image: mintplexlabs/anythingllm:latest
restart: unless-stopped
expose:
ports:
- 3001
environment:
- STORAGE_DIR=/app/server/storage
@@ -12,4 +13,4 @@ services:
- SYS_ADMIN
volumes:
storage: {}
storage: {}
-19
View File
@@ -1,19 +0,0 @@
{
"id": "anythingllm",
"name": "AnythingLLM",
"version": "latest",
"description": "AnythingLLM is a private AI chat app for working with your own documents. Self-host it on your own server with Dokploy, which deploys AnythingLLM as a Docker Compose service in a few clicks.",
"logo": "logo.png",
"links": {
"github": "https://github.com/Mintplex-Labs/anything-llm",
"website": "https://anythingllm.com/",
"docs": "https://docs.anythingllm.com/"
},
"tags": [
"ai",
"llm",
"chatbot"
],
"seoTitle": "Self-Host AnythingLLM in minutes",
"seoDescription": "Private AI chatbot for your documents. Self-host AnythingLLM on Dokploy in minutes – no manual setup needed."
}
+1 -2
View File
@@ -1,4 +1,3 @@
# dokploy: allow-host-ports — Anytype clients speak the any-sync protocol (TCP 33010, UDP/QUIC 33020) directly against the server; it is not HTTP, so Traefik cannot route it.
# Example: Any-Sync-Bundle with embedded MongoDB and Redis (all-in-one image)
#
# Usage:
@@ -14,7 +13,7 @@ services:
- "33010:33010"
- "33020:33020/udp"
volumes:
- ../files/anytype-data:/data
- ./data:/data
environment:
# Advertise addresses clients should use. Replace with your server hostname/IP.
ANY_SYNC_BUNDLE_INIT_EXTERNAL_ADDRS: "192.168.100.9"
-19
View File
@@ -1,19 +0,0 @@
{
"id": "anytype",
"name": "Anytype",
"version": "latest",
"description": "Anytype is a private, offline-first app for notes and personal knowledge. Self-host it on your own server with Dokploy, which deploys Anytype as a Docker Compose service in a few clicks.",
"logo": "logo.png",
"links": {
"github": "https://github.com/grishy/any-sync-bundle",
"docs": "https://doc.anytype.io/anytype-docs",
"website": "https://anytype.io/"
},
"tags": [
"note-taking",
"local-first",
"peer-to-peer"
],
"seoTitle": "Self-Host Anytype with Dokploy",
"seoDescription": "Private, offline-first personal knowledge base. Self-host Anytype on Dokploy – view your client config afterward with a quick command inside the container."
}
-21
View File
@@ -1,21 +0,0 @@
{
"id": "appflowy",
"name": "App Flowy",
"version": "0.9.3",
"description": "AppFlowy is a workspace app for notes, docs, and projects, and an open-source alternative to Notion. Self-host it on your own server with Dokploy, which deploys AppFlowy as a Docker Compose service in a few clicks.",
"links": {
"github": "https://github.com/AppFlowy-IO/AppFlowy",
"website": "https://appflowy.io/",
"docs": "https://docs.appflowy.io/docs"
},
"logo": "appflowy.png",
"tags": [
"productivity",
"self-hosted",
"notes",
"knowledge-base",
"notion-alternative"
],
"seoTitle": "Self-Host AppFlowy in minutes",
"seoDescription": "Workspace app, an alternative to Notion. Self-host AppFlowy on Dokploy in minutes – no manual setup needed."
}
+3 -2
View File
@@ -1,8 +1,9 @@
version: "3.8"
services:
apprise-api:
image: linuxserver/apprise-api:latest
restart: unless-stopped
expose:
ports:
- 8000
environment:
- PUID=1000
@@ -12,4 +13,4 @@ services:
- config:/config
volumes:
config: {}
config: {}
-16
View File
@@ -1,16 +0,0 @@
{
"id": "apprise-api",
"name": "Apprise API",
"version": "latest",
"description": "Apprise API provides a simple interface for sending notifications to almost all of the most popular notification services available to us today.",
"logo": "logo.png",
"links": {
"github": "https://github.com/caronc/apprise-api",
"website": "https://github.com/caronc/apprise-api",
"docs": "https://github.com/caronc/apprise-api/wiki"
},
"tags": [
"notifications",
"api"
]
}
+3 -6
View File
@@ -1,9 +1,6 @@
version: "3.8"
services:
appsmith:
image: appsmith/appsmith-ee:v1.94
image: index.docker.io/appsmith/appsmith-ee:v1.29
volumes:
- appsmith-data:/appsmith-stacks
restart: unless-stopped
volumes:
appsmith-data:
- ../files/stacks:/appsmith-stacks
-15
View File
@@ -1,15 +0,0 @@
{
"id": "appsmith",
"name": "Appsmith",
"version": "v1.94",
"description": "Appsmith is a free and open source platform for building internal tools and applications.",
"logo": "appsmith.png",
"links": {
"github": "https://github.com/appsmithorg/appsmith",
"website": "https://appsmith.com/",
"docs": "https://docs.appsmith.com/"
},
"tags": [
"cms"
]
}
File diff suppressed because it is too large Load Diff
-20
View File
@@ -1,20 +0,0 @@
{
"id": "appwrite",
"name": "Appwrite",
"version": "2.5.0",
"description": "Appwrite is an end-to-end platform for building Web, Mobile, Native, or Backend apps, packaged as a set of Docker microservices. It includes both a backend server and a fully integrated hosting solution for deploying static and server-side rendered frontends. Appwrite abstracts the complexity and repetitiveness required to build modern apps from scratch and allows you to build secure, full-stack applications faster.\nUsing Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.",
"links": {
"github": "https://github.com/appwrite/appwrite",
"website": "https://appwrite.io/",
"docs": "https://appwrite.io/docs"
},
"logo": "appwrite.svg",
"tags": [
"database",
"firebase",
"postgresql",
"mongodb",
"hosting",
"self-hosted"
]
}
+56 -370
View File
@@ -1,121 +1,51 @@
[variables]
main_domain = "${domain}"
functions_domain = "functions.${main_domain}"
sites_domain = "sites.${main_domain}"
mqtt_domain = "mqtt.${main_domain}"
openssl_key = "${password:32}"
notifications_tracking_secret = "${password:32}"
db_pw = "${password:32}"
db_root_pw = "${password:32}"
db_user_pw = "${password:32}"
usage_pw = "${password:32}"
executor_secret = "${password:32}"
jobs_secret = "${password:32}"
geo_secret = "${password:32}"
console_domain = "console.${main_domain}"
[config]
# Build jobs and runtimes join <appName>_default, which isolated deployments never create
# (every service sits on the external <appName> network instead).
isolated = false
env = [
"_APP_ENV=production",
"_APP_EDITION=self-hosted",
"_APP_LOCALE=en",
"_APP_WORKER_PER_CORE=6",
"_APP_OPTIONS_ABUSE=enabled",
"_APP_OPTIONS_ABUSE_INCREASED_LIMIT_PROJECTS=",
"_APP_OPTIONS_FORCE_HTTPS=disabled",
"_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled",
"_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled",
"_APP_OPTIONS_ROUTER_PROTECTION=disabled",
"_APP_ROUTER_AUTO_CERTIFICATES=enabled",
"_APP_LOCKING_ENABLED=enabled",
"_APP_OPENSSL_KEY_V1=${openssl_key}",
"_APP_NOTIFICATIONS_TRACKING_SECRET=${notifications_tracking_secret}",
"_APP_OPENSSL_KEY_V1=your-secret-key",
"_APP_DOMAIN=${main_domain}",
"_APP_CONSOLE_DOMAIN=${console_domain}",
"_APP_CONSOLE_URL=https://${console_domain}",
"_APP_CONSOLE_HOSTNAMES=${console_domain}",
"_APP_CONSOLE_TRUSTED_PROJECTS=",
"_APP_CUSTOM_DOMAIN_DENY_LIST=example.com,test.com,app.example.com",
"_APP_DOMAIN_FUNCTIONS=${functions_domain}",
"_APP_DOMAIN_SITES=${sites_domain}",
"_APP_DOMAIN_TARGET_CNAME=localhost",
"_APP_DOMAIN_TARGET_AAAA=::1",
"_APP_DOMAIN_TARGET_A=127.0.0.1",
"_APP_DOMAIN_TARGET_CAA=",
"_APP_DNS=8.8.8.8",
"_APP_TRUSTED_HEADERS=x-forwarded-for",
"_APP_DOMAIN_FUNCTIONS=${main_domain}",
"_APP_DOMAIN_TARGET=${main_domain}",
"_APP_CONSOLE_WHITELIST_ROOT=enabled",
"_APP_CONSOLE_WHITELIST_EMAILS=",
"_APP_CONSOLE_WHITELIST_IPS=",
"_APP_CONSOLE_HOSTNAMES=",
"_APP_CONSOLE_SCHEMA=appwriteio",
"_APP_CONSOLE_SESSION_ALERTS=disabled",
"_APP_CONSOLE_COUNTRIES_DENYLIST=",
"_APP_CONSOLE_GITHUB_APP_ID=",
"_APP_CONSOLE_GITHUB_SECRET=",
"_APP_CONSOLE_GITLAB_APP_ID=",
"_APP_CONSOLE_GITLAB_SECRET=",
"_APP_CONSOLE_GITLAB_ENDPOINT=",
"_APP_CONSOLE_BITBUCKET_APP_ID=",
"_APP_CONSOLE_BITBUCKET_SECRET=",
"_APP_CONSOLE_GOOGLE_APP_ID=",
"_APP_CONSOLE_GOOGLE_SECRET=",
"_APP_SYSTEM_EMAIL_NAME=Appwrite",
"_APP_SYSTEM_EMAIL_ADDRESS=noreply@appwrite.io",
"_APP_SYSTEM_TEAM_EMAIL=team@appwrite.io",
"_APP_SYSTEM_RESPONSE_FORMAT=",
"_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=certs@appwrite.io",
"_APP_EMAIL_SECURITY=",
"_APP_EMAIL_CERTIFICATES=",
"_APP_COMPRESSION_ENABLED=enabled",
"_APP_COMPRESSION_MIN_SIZE_BYTES=1024",
"_APP_USAGE_STATS=enabled",
"_APP_LOGGING_PROVIDER=",
"_APP_LOGGING_CONFIG=",
"_APP_LOGGING_FORMAT=pretty",
"_APP_GEO_ENDPOINT=http://appwrite-geo/v1",
"_APP_GEO_SECRET=${geo_secret}",
"_APP_USAGE_AGGREGATION_INTERVAL=30",
"_APP_USAGE_TIMESERIES_INTERVAL=30",
"_APP_USAGE_DATABASE_INTERVAL=900",
"_APP_WORKER_PER_CORE=6",
"_APP_CONSOLE_SESSION_ALERTS=disabled",
"_APP_REDIS_HOST=redis",
"_APP_REDIS_PORT=6379",
"_APP_REDIS_USER=",
"_APP_REDIS_PASS=",
"_APP_DB_ADAPTER=postgresql",
"_APP_DB_HOST=postgresql",
"_APP_DB_PORT=5432",
"_APP_DB_HOST=mariadb",
"_APP_DB_PORT=3306",
"_APP_DB_SCHEMA=appwrite",
"_APP_DB_USER=user",
"_APP_DB_PASS=${db_pw}",
"_APP_DATABASE_SHARED_TABLES=",
"_APP_DATABASE_SHARED_NAMESPACE=",
"_APP_LIMIT_DATABASE_BATCH=100",
"_APP_DOCUMENTSDB=enabled",
"_APP_DB_ADAPTER_DOCUMENTSDB=mongodb",
"_APP_DB_HOST_DOCUMENTSDB=mongodb",
"_APP_DB_PORT_DOCUMENTSDB=27017",
"_APP_DB_SCHEMA_DOCUMENTSDB=",
"_APP_DB_USER_DOCUMENTSDB=",
"_APP_DB_PASS_DOCUMENTSDB=",
"_APP_CONNECTIONS_DATABASE_DOCUMENTSDB=",
"_APP_VECTORSDB=enabled",
"_APP_DB_ADAPTER_VECTORSDB=postgresql",
"_APP_DB_HOST_VECTORSDB=postgresql",
"_APP_DB_PORT_VECTORSDB=5432",
"_APP_DB_SCHEMA_VECTORSDB=",
"_APP_DB_USER_VECTORSDB=",
"_APP_DB_PASS_VECTORSDB=",
"_APP_CONNECTIONS_DATABASE_VECTORSDB=",
"_APP_EMBEDDING=disabled",
"_APP_USAGE_STATS=enabled",
"_APP_USAGE_PASS=${usage_pw}",
"_APP_CONNECTIONS_DB_USAGE=http://appwrite:${usage_pw}@clickhouse:8123/appwrite",
"_APP_POOL_SIZE_USAGE=2",
"_APP_CONNECTIONS_DB_EXECUTIONS=http://appwrite:${usage_pw}@clickhouse:8123/appwrite",
"_APP_POOL_SIZE_EXECUTIONS=2",
"_APP_MAINTENANCE_RETENTION_USAGE_TTL=180",
"_APP_STATS_USAGE_QUEUE_NAME=v1-stats-usage",
"_APP_STATS_RESOURCES_QUEUE_NAME=v1-stats-resources",
"_APP_STATS_RESOURCES_INTERVAL=3600",
"_APP_MQTT_REPLAY_DEPTH=5",
"_APP_MQTT_MAX_PACKET_SIZE=64000",
"_APP_DB_PASS=password",
"_APP_DB_ROOT_PASS=rootsecretpassword",
"_APP_INFLUXDB_HOST=influxdb",
"_APP_INFLUXDB_PORT=8086",
"_APP_STATSD_HOST=telegraf",
"_APP_STATSD_PORT=8125",
"_APP_SMTP_HOST=",
"_APP_SMTP_PORT=",
"_APP_SMTP_SECURE=",
@@ -123,11 +53,8 @@ env = [
"_APP_SMTP_PASSWORD=",
"_APP_SMS_PROVIDER=",
"_APP_SMS_FROM=",
"_APP_SMS_PROJECTS_DENY_LIST=",
"_APP_PWNED_PASSWORDS_DSN=none://localhost",
"_APP_STORAGE_LIMIT=30000000",
"_APP_STORAGE_PREVIEW_LIMIT=20000000",
"_APP_AUTOGRAVITY_HOST=http://appwrite-autogravity:8080",
"_APP_STORAGE_ANTIVIRUS=disabled",
"_APP_STORAGE_ANTIVIRUS_HOST=clamav",
"_APP_STORAGE_ANTIVIRUS_PORT=3310",
@@ -136,7 +63,6 @@ env = [
"_APP_STORAGE_S3_SECRET=",
"_APP_STORAGE_S3_REGION=us-east-1",
"_APP_STORAGE_S3_BUCKET=",
"_APP_STORAGE_S3_ENDPOINT=",
"_APP_STORAGE_DO_SPACES_ACCESS_KEY=",
"_APP_STORAGE_DO_SPACES_SECRET=",
"_APP_STORAGE_DO_SPACES_REGION=us-east-1",
@@ -153,305 +79,65 @@ env = [
"_APP_STORAGE_WASABI_SECRET=",
"_APP_STORAGE_WASABI_REGION=eu-central-1",
"_APP_STORAGE_WASABI_BUCKET=",
"_APP_COMPUTE_SIZE_LIMIT=30000000",
"_APP_COMPUTE_BUILD_SIZE_LIMIT=2000000000",
"_APP_COMPUTE_BUILD_TIMEOUT=2700",
"_APP_COMPUTE_CPUS=0",
"_APP_COMPUTE_MEMORY=0",
"_APP_COMPUTE_INACTIVE_THRESHOLD=60",
"_APP_COMPUTE_MAINTENANCE_INTERVAL=3600",
"_APP_OPEN_RUNTIMES_NFT=enabled",
"_APP_FUNCTIONS_SIZE_LIMIT=30000000",
"_APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000",
"_APP_FUNCTIONS_TIMEOUT=900",
"_APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000",
"_APP_FUNCTIONS_SCHEDULE_SPREAD=0",
"_APP_FUNCTIONS_RUNTIMES=node-22,node-24,node-26,python-3.12,php-8.3,ruby-3.3,dart-3.5,go-1.23,rust-1.83,bun-1.1,deno-1.46,java-21.0,dotnet-8.0",
"_APP_SITES_TIMEOUT=900",
"_APP_SITES_RUNTIMES=static-1,node-22",
"_APP_EXECUTOR_SECRET=${executor_secret}",
"_APP_EXECUTOR_HOST=http://openruntimes-executor/v1",
"_APP_EXECUTOR_IMAGES=openruntimes/node:v5-22,openruntimes/python:v5-3.12,openruntimes/php:v5-8.3,openruntimes/static:v5-1",
"_APP_EXECUTOR_CONNECTION_STORAGE=local://localhost",
"_APP_JOBS_HOST=http://orchestrator:8080",
"_APP_JOBS_SECRET=${jobs_secret}",
"_APP_JOBS_ENDPOINT=http://appwrite",
"_APP_BROWSER_HOST=http://appwrite-browser:3000/v1",
"_APP_WORKER_SCREENSHOTS_ROUTER=http://appwrite",
"_APP_FUNCTIONS_BUILD_TIMEOUT=900",
"_APP_FUNCTIONS_CONTAINERS=10",
"_APP_FUNCTIONS_CPUS=0",
"_APP_FUNCTIONS_MEMORY=0",
"_APP_FUNCTIONS_MEMORY_SWAP=0",
"_APP_FUNCTIONS_RUNTIMES=node-16.0,php-8.0,python-3.9,ruby-3.0",
"_APP_EXECUTOR_SECRET=your-secret-key",
"_APP_EXECUTOR_HOST=http://exc1/v1",
"_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes",
"_APP_FUNCTIONS_ENVS=node-16.0,php-7.4,python-3.9,ruby-3.0",
"_APP_FUNCTIONS_INACTIVE_THRESHOLD=60",
"DOCKERHUB_PULL_USERNAME=",
"DOCKERHUB_PULL_PASSWORD=",
"DOCKERHUB_PULL_EMAIL=",
"OPEN_RUNTIMES_NETWORK=appwrite_runtimes",
"_APP_FUNCTIONS_RUNTIMES_NETWORK=runtimes",
"_APP_DOCKER_HUB_USERNAME=",
"_APP_DOCKER_HUB_PASSWORD=",
"_APP_MAINTENANCE_INTERVAL=86400",
"_APP_MAINTENANCE_START_TIME=00:00",
"_APP_MAINTENANCE_RETENTION_CACHE=2592000",
"_APP_MAINTENANCE_RETENTION_EXECUTION=1209600",
"_APP_MAINTENANCE_RETENTION_AUDIT=1209600",
"_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=15778800",
"_APP_MAINTENANCE_RETENTION_ABUSE=86400",
"_APP_MAINTENANCE_RETENTION_SCHEDULES=86400",
"_APP_INTERVAL_DOMAIN_VERIFICATION=120",
"_APP_INTERVAL_CLEANUP_STALE_EXECUTIONS=300",
"_APP_GRAPHQL_INTROSPECTION=enabled",
"_APP_GRAPHQL_MAX_BATCH_SIZE=10",
"_APP_GRAPHQL_MAX_COMPLEXITY=250",
"_APP_GRAPHQL_MAX_DEPTH=4",
"_APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10",
"_APP_FUNCTIONS_MAINTENANCE_INTERVAL=3600",
"_APP_VCS_GITHUB_APP_NAME=",
"_APP_VCS_GITHUB_PRIVATE_KEY=",
"_APP_VCS_GITHUB_APP_ID=",
"_APP_VCS_GITHUB_CLIENT_ID=",
"_APP_VCS_GITHUB_CLIENT_SECRET=",
"_APP_VCS_GITHUB_WEBHOOK_SECRET=",
"_APP_VCS_GITEA_ENDPOINT=",
"_APP_VCS_GITEA_BROWSER_ENDPOINT=",
"_APP_VCS_GITEA_CLIENT_ID=",
"_APP_VCS_GITEA_CLIENT_SECRET=",
"_APP_VCS_GITEA_WEBHOOK_SECRET=",
"_APP_VCS_GITLAB_ENDPOINT=",
"_APP_VCS_GITLAB_CLIENT_ID=",
"_APP_VCS_GITLAB_CLIENT_SECRET=",
"_APP_VCS_GITLAB_WEBHOOK_SECRET=",
"_APP_VCS_BITBUCKET_CLIENT_ID=",
"_APP_VCS_BITBUCKET_CLIENT_SECRET=",
"_APP_VCS_BITBUCKET_WEBHOOK_SECRET=",
"_APP_VCS_ORIGIN_CLIENT_ID=",
"_APP_VCS_ORIGIN_PRIVATE_KEY=",
"_APP_VCS_WEBHOOK_URL=",
"_APP_MIGRATION_HOST=appwrite",
"_APP_MAINTENANCE_INTERVAL=86400",
"_APP_MAINTENANCE_DELAY=0",
"_APP_MAINTENANCE_RETENTION_CACHE=2592000",
"_APP_MAINTENANCE_RETENTION_EXECUTION=1209600",
"_APP_MAINTENANCE_RETENTION_AUDIT=1209600",
"_APP_MAINTENANCE_RETENTION_ABUSE=86400",
"_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000",
"_APP_MAINTENANCE_RETENTION_SCHEDULES=86400",
"_APP_GRAPHQL_MAX_BATCH_SIZE=10",
"_APP_GRAPHQL_MAX_COMPLEXITY=250",
"_APP_GRAPHQL_MAX_DEPTH=3",
"_APP_MIGRATIONS_FIREBASE_CLIENT_ID=",
"_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=",
"_APP_DB_ROOT_PASS=${db_root_pw}",
"_APP_DB_USER_PASS=${db_user_pw}",
"_APP_ASSISTANT_OPENAI_API_KEY=",
]
[[config.mounts]]
filePath = "mongo-init.js"
content = """// mongo-init.js
const adminDb = db.getSiblingDB('admin');
const username = process.env.MONGO_INITDB_USERNAME;
const password = process.env.MONGO_INITDB_PASSWORD;
const database = process.env.MONGO_INITDB_DATABASE;
// Idempotent: the official entrypoint races the real server on port 27017 when
// initialising a fresh volume (appwrite/appwrite#13593), so this script may run
// more than once. Skip if the user already exists.
if (adminDb.getUser(username) === null) {
adminDb.createUser({
user: username,
pwd: password,
roles: [
{ role: 'readWrite', db: database }
]
});
}
"""
[[config.mounts]]
filePath = "mongo-entrypoint.sh"
content = """#!/bin/bash
set -e
KEYFILE_PATH="/data/keyfile/mongo-keyfile"
if [ ! -f "$KEYFILE_PATH" ]; then
echo "Generating random MongoDB keyfile..."
mkdir -p /data/keyfile
openssl rand -base64 756 > "$KEYFILE_PATH"
fi
chmod 400 "$KEYFILE_PATH"
chown mongodb:mongodb "$KEYFILE_PATH" 2>/dev/null || chown 999:999 "$KEYFILE_PATH"
# The standard entrypoint initialises an empty data directory with a second
# mongod on 27017, stopped with `mongod --shutdown`. That returns once the
# server clears <dbpath>/mongod.lock, several steps before it exits and
# releases the port, and the real server binds a millisecond after it starts --
# so a slow shutdown tail leaves it exiting 48, "Address already in use",
# taking the container with it (appwrite/appwrite#13593). Initialise here
# instead, on a port the real server never binds, and hand over a directory
# the standard entrypoint has nothing left to do.
INIT_MARKER=/data/db/.appwrite-initialised
if [ ! -e "$INIT_MARKER" ]; then
INIT_PORT=27018
echo "Initialising MongoDB data directory..."
find -L /data/db \\! -user mongodb -exec chown mongodb '{}' +
export MONGO_INITDB_DATABASE="${MONGO_INITDB_DATABASE:-test}"
gosu mongodb mongod --dbpath /data/db --bind_ip 127.0.0.1 --port "$INIT_PORT" &
INIT_SERVER=$!
READY=
for _ in $(seq 1 30); do
if mongosh --host 127.0.0.1 --port "$INIT_PORT" --quiet --eval 'quit(0)' > /dev/null 2>&1; then
READY=1
break
fi
if ! kill -0 "$INIT_SERVER" 2>/dev/null; then
break
fi
sleep 1
done
if [ -z "$READY" ]; then
echo "MongoDB did not accept connections for initialisation" >&2
exit 1
fi
mongosh --host 127.0.0.1 --port "$INIT_PORT" --quiet admin --eval '
const username = process.env.MONGO_INITDB_ROOT_USERNAME;
if (db.getUser(username) === null) {
db.createUser({
user: username,
pwd: process.env.MONGO_INITDB_ROOT_PASSWORD,
roles: [{ role: "root", db: "admin" }]
});
}
'
mongosh --host 127.0.0.1 --port "$INIT_PORT" --quiet "$MONGO_INITDB_DATABASE" /mongo-init.js
touch "$INIT_MARKER"
kill -TERM "$INIT_SERVER"
wait "$INIT_SERVER"
echo "MongoDB data directory initialised."
fi
exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --auth --keyFile "$KEYFILE_PATH"
"""
[[config.mounts]]
filePath = "clickhouse-config.xml"
content = """<?xml version="1.0"?>
<!--
Retention for ClickHouse's own system log tables.
Every <ttl> in the image's config.xml sits inside an XML comment, so these
tables keep every row forever, and the metric ones add a row per second
whether or not the instance serves traffic. Left alone they outgrow the
usage data they sit next to and fill the volume.
When a window here changes, ClickHouse replaces the table on the next
restart and keeps the old one, rows and all, as <log>_<N>.
clickhouse-init.sh gives those old tables the same window.
-->
<clickhouse>
<!-- The metric, trace and profile logs sample the server every second whether
or not Appwrite serves traffic, and nothing reads them back, so their
writes and the merges behind them are pure overhead. -->
<asynchronous_metric_log remove="1"/>
<metric_log remove="1"/>
<histogram_metric_log remove="1"/>
<query_metric_log remove="1"/>
<processors_profile_log remove="1"/>
<trace_log remove="1"/>
<!-- text_log repeats the server log the container already writes. -->
<text_log remove="1"/>
<!-- Kept for debugging, capped so they cannot fill the volume. -->
<asynchronous_insert_log><ttl>event_date + INTERVAL 3 DAY DELETE</ttl></asynchronous_insert_log>
<query_log><ttl>event_date + INTERVAL 7 DAY DELETE</ttl></query_log>
<query_views_log><ttl>event_date + INTERVAL 7 DAY DELETE</ttl></query_views_log>
<query_thread_log><ttl>event_date + INTERVAL 7 DAY DELETE</ttl></query_thread_log>
<part_log><ttl>event_date + INTERVAL 7 DAY DELETE</ttl></part_log>
<background_schedule_pool_log><ttl>event_date + INTERVAL 7 DAY DELETE</ttl></background_schedule_pool_log>
<!-- Longer, so it still covers a report that reaches us late. crash_log is
deliberately left alone: it is tiny and worth keeping. -->
<error_log><ttl>event_date + INTERVAL 14 DAY DELETE</ttl></error_log>
</clickhouse>
"""
[[config.mounts]]
filePath = "clickhouse-init.sh"
content = """#!/bin/bash
# The ClickHouse entrypoint sources this file, so a subshell keeps these options out of it.
(
set -euo pipefail
client() {
clickhouse-client --host 127.0.0.1 --user "$CLICKHOUSE_USER" --password "$CLICKHOUSE_PASSWORD" "$@"
}
# Flushing replaces log tables whose definition changed, keeping each old table as <log>_<N>.
client --query 'SYSTEM FLUSH LOGS'
# Tables left behind never expire on their own: take the TTL of the log that
# replaced this one, or a week when the log is no longer written at all.
# The named logs must match the ones clickhouse-config.xml removes: a log that
# is still written gets replaced on the next restart if its TTL is altered here.
client --format TSVRaw <<'SQL' | client --multiquery
SELECT concat('ALTER TABLE system.', stale.name, ' MODIFY TTL ', if(active.name = '', 'event_date + INTERVAL 7 DAY DELETE', splitByString(' SETTINGS ', splitByString(' TTL ', active.engine_full)[2])[1]), ';')
FROM (
SELECT name, splitByChar('_', name)[-1] AS suffix, arrayStringConcat(arrayPopBack(splitByChar('_', name)), '_') AS log
FROM system.tables
WHERE database = 'system'
AND position(engine_full, ' TTL ') = 0
AND endsWith(engine, 'MergeTree')
AND name IN (SELECT table FROM system.columns WHERE database = 'system' AND name = 'event_date' AND type = 'Date')
) AS stale
LEFT JOIN system.tables AS active
ON active.database = 'system'
AND active.name = stale.log
AND position(active.engine_full, ' TTL ') > 0
WHERE (endsWith(stale.log, '_log') AND toUInt64OrNull(stale.suffix) IS NOT NULL)
OR stale.name IN ('asynchronous_metric_log', 'metric_log', 'histogram_metric_log', 'query_metric_log', 'processors_profile_log', 'trace_log', 'text_log')
SQL
)
"""
mounts = []
[[config.domains]]
serviceName = "appwrite-console"
port = 3000
serviceName = "appwrite"
port = 80
host = "${main_domain}"
path = "/"
[[config.domains]]
serviceName = "appwrite"
serviceName = "appwrite-console"
port = 80
host = "${main_domain}"
path = "/v1"
[[config.domains]]
serviceName = "appwrite"
port = 80
host = "${main_domain}"
path = "/.well-known"
path = "/console"
[[config.domains]]
serviceName = "appwrite-realtime"
port = 80
host = "${main_domain}"
path = "/v1/realtime"
# --- Dedicated Console domain ---
# The Console SPA is served at the root of ${console_domain}, with /v1 and
# /v1/realtime proxied to the API and Realtime so the Console reaches the API
# on its own origin.
[[config.domains]]
serviceName = "appwrite-console"
port = 3000
host = "${console_domain}"
path = "/"
[[config.domains]]
serviceName = "appwrite"
port = 80
host = "${console_domain}"
path = "/v1"
[[config.domains]]
serviceName = "appwrite-realtime"
port = 80
host = "${console_domain}"
path = "/v1/realtime"
[[config.domains]]
serviceName = "appwrite"
port = 80
host = "${sites_domain}"
path = "/"
[[config.domains]]
serviceName = "appwrite"
port = 80
host = "${functions_domain}"
path = "/"
[[config.domains]]
serviceName = "appwrite-mqtt"
port = 8083
host = "${mqtt_domain}"
path = "/"
-16
View File
@@ -1,16 +0,0 @@
{
"id": "aptabase",
"name": "Aptabase",
"version": "v1.0.0",
"description": "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.",
"logo": "aptabase.svg",
"links": {
"github": "https://github.com/aptabase/aptabase",
"website": "https://aptabase.com/",
"docs": "https://github.com/aptabase/aptabase/blob/main/README.md"
},
"tags": [
"analytics",
"self-hosted"
]
}
+3 -2
View File
@@ -1,8 +1,9 @@
version: "3.8"
services:
arangodb:
image: arangodb:3.12.4
restart: unless-stopped
expose:
ports:
- 8529
environment:
- ARANGO_ROOT_PASSWORD=${ARANGO_PASSWORD}
@@ -10,4 +11,4 @@ services:
- data:/var/lib/arangodb3
volumes:
data: {}
data: {}
-17
View File
@@ -1,17 +0,0 @@
{
"id": "arangodb",
"name": "ArangoDB",
"version": "latest",
"description": "ArangoDB is a native multi-model database with flexible data models for documents, graphs, and key-values. Build high performance applications using a convenient SQL-like query language or JavaScript extensions.",
"logo": "logo.png",
"links": {
"github": "https://github.com/arangodb/arangodb",
"website": "https://www.arangodb.com/",
"docs": "https://www.arangodb.com/docs/"
},
"tags": [
"database",
"graph-database",
"nosql"
]
}
-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="1000"><g clip-path="url(#SvgjsClipPath1152)"><rect width="1000" height="1000" fill="#000000"></rect><g transform="matrix(3.401360544217687,0,0,3.401360544217687,250.00000000000003,265.30612244897964)"><svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="147" height="138" viewBox="0 0 147 138"><image width="147" height="138" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJMAAACKCAYAAACuPHhGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAZgSURBVHgB7Z3rddNAEIVHVBA6MB1AB04H6QCnAuiAQwehgogOoALUAXSAO0g6MBqzCvip1+zundX9zvEJIfkRS1efVrM760oCu91u0355FELGUVdVda//eCWEGMEwETMYJmIGw0TMYJiIGQwTMYNhImYwTMQMhomYwTARMxgmYgbDRMxgmIgZDBMxg2EiZjBMxAyGiZjBMBEzGCZiBsNEzGCYiBkMEzGDYSJmMEzEDIaJmMEwETMYJmIGw0TMYJiIGQwTMYNhImZUUgi73W7VfvktvnhuX2+qqnqWAijJTB/EHzft66MUQhFmClb60b5W4o9i7FSKmd6LzyApxdjJvZlaK+nJ+Cl+w6QUYacSzHQnvoOkFGGnEsykT3Ar8Y97O7k2U9hueiVl4N5Ors1UkJU6nlszvRanuDVTYVbquAnvyyVuzVSglTq2rZ3eiENcmqlQK3WsvNrJpZkKtlJH09rpVpzhzkyFW6lj3b7PtTjDnZkWYKUOd3ZyZaaFWKnDnZ283ebey7L4JI5wE6Zwla5lWbiykyczubpKDXHzvl2EaaFW6nBjJy9mWtpY6RgXdoIvDThtFIjBbVsqaAQYD2Za6ljpGPjjAG0mWukEXTy3FVDQzUQrHQJ9PGDNRCtdBNZOyGailc4Du7QX0ky00lVgGw9QzUQrXQa28QDOTLTSICDthGgmWqkfSDtBmcn5BhSpgbMTmplQN6D4JnjA2QnGTOBW0tajR8FbuQBlJyQzrQUzSHUoEn4VPKDshGQm1EaBfcU5bN2jf+ONYAFjJwgzATcKdFaScLK+CB4wdoIwE7qVum9op+tkNxOwlZrjCVVwO91JZrKbCdhKZ1c2Alfos294kdVMwFbaXloiG2zVCB7ZN7zIfZtDnTr5PPPnuci6F3q2MIFbqb72C8FajeDxNmdbVE4zebXS2N9LTbbjmiVMyFaSgfNwwU6/BI9sTZu5zITaVNmMrNUgTrEoWeyUvDQQrpofgsmoxfrARUzltkrctJnDTKhjpboa2fUBXMRUkh/npGYqyUodtNM/UpsJdaxUVxN70YKdOHaShGYCbxSYdQWX/N7GkNJMqGOlZu7BBp5iUZId9yRhClfuRjCxukWhFjGT1Z1SmQnVSr1TJ0MBnmJRkoxVo4+ZwMcT91ZhUkp8Wh1DCjMVb6WOYKetYBL9PEQNE/hYKVaxEbWIuQk1sWjENhNyq3esxspa/q7JRiRq40G0MIFbqY41fgCfYvkQ004xzYRspdiP8Q+CaaeobVFRwhSstBZM6thPNeBTLNHsFMtMqBtQKKmKi7VgEs1O5nUm8A0okn6GW3ss9DisBY8oTZsxzKTNgCvBJPWUB+oUSxQ7xTATalNllibFJdnJ1Ezgn1SZyxKoZQJzO5maiVY6T3tcngRzJaapnczMRCtdBdlOGzHCzEzIVmpf76qM282ArxM3s7aJmcCt1FSZ9y0Cn2Ix2/DCxEzAVlKir+MZwhLsNNtM4FaqEYKkBDt9F0xWFkt7Z5uJVhoO+ErM2bMDs8xEK40DfJ347MaDube5rJtL9YA6a486xaLMWjY0OUwhxW8Fk9m9cLEo2U5zzIS8+A3VSh3If9/k8zppAA4+kMy+6+wQgKdYlEkt5VPNhDxWQh6T/A9qEVOZZKfRZgJvqnRhJQW8iKmMttMUMyGPlZCv9gPAp1iU0ed5lJkcfH4uVJGyj2CnJ8FllJ3GmgnZSnBFyj7Au1iUURteDDYTrRQH8CdjZfBxHWMmWikC4EVMZfB5H2QmWikuDuz0esiasKFmQrZS4zlIigM7DWo86DWTAytNqtaiEVZgPAomgxoPhpgJ2UrbEoIU+Ca4W/EMaou6GibwbXEUL1MnvTgoYvZueNFnpiQba07EfBtBAB7EsZ0uholWSo93O10zE/K2OFvBfvqZw4PgctVO18K0EVzclwMuEezUCC4X7XQ2TOCNAkpxt7gjkN/fRTudrTOBty/p1Mm9FA7wVjzK2brTiZloJRjQ7bQ5/s8TM9FKOLTn4qfgdgCdrGo9MJMDK6F3nViD/H5PNrw4MBO4lZJuboqAg3XiB3Z6MROthIeDIqba6a775sVM4FZy03VijQM7vdwx9mbiExwuDtaJv7SU781EK2HjYE3Z3k6vQqpWgsvixkrHgH+gtLK3UwVeaVVcr++2wsE68eYPiYOoQetKl74AAAAASUVORK5CYII="></image></svg></g></g><defs><clipPath id="SvgjsClipPath1152"><rect width="1000" height="1000" x="0" y="0" rx="350" ry="350"></rect></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 2.9 KiB

-100
View File
@@ -1,100 +0,0 @@
# Arche — AI workspace platform that spawns per-user OpenCode containers
# via a Docker socket proxy.
#
# PRE-REQUISITES (run once on the VPS before deploying):
# mkdir -p /opt/arche/kb-content /opt/arche/kb-config /opt/arche/users
#
# Optional (only needed to seed the Knowledge Base):
# git init --bare --initial-branch=main /opt/arche/kb-content
# git init --bare --initial-branch=main /opt/arche/kb-config
#
# POST-DEPLOY (set in Dokploy → Environment, then redeploy):
# 1. Deploy this template once. Dokploy auto-creates a network for the
# compose stack.
# 2. SSH into the host and find the network name:
# docker network ls --format '{{.Name}}' | grep arche
# It looks like `<stack-name>_default` or similar.
# 3. Add the env var OPENCODE_NETWORK=<that-network-name> and redeploy.
# Without it, spawned user workspaces will fail to start.
#
# IMAGE ARCHITECTURE:
# Default tags target linux/amd64. For ARM64 hosts (Apple Silicon,
# Ampere, Graviton), override both images to the `-arm64` variants in
# Dokploy → Environment:
# ARCHE_WEB_IMAGE=ghcr.io/peaberry-studio/arche/web:latest-arm64
# ARCHE_WORKSPACE_IMAGE=ghcr.io/peaberry-studio/arche/workspace:latest-arm64
services:
web:
image: ${ARCHE_WEB_IMAGE:-ghcr.io/peaberry-studio/arche/web:latest}
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
docker-socket-proxy:
condition: service_started
environment:
ARCHE_DOMAIN: ${ARCHE_DOMAIN}
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD}@postgres:5432/arche?schema=public
ARCHE_SESSION_PEPPER: ${ARCHE_SESSION_PEPPER}
ARCHE_ENCRYPTION_KEY: ${ARCHE_ENCRYPTION_KEY}
ARCHE_INTERNAL_TOKEN: ${ARCHE_INTERNAL_TOKEN}
ARCHE_GATEWAY_TOKEN_SECRET: ${ARCHE_GATEWAY_TOKEN_SECRET}
ARCHE_CONNECTOR_OAUTH_STATE_SECRET: ${ARCHE_CONNECTOR_OAUTH_STATE_SECRET}
ARCHE_GATEWAY_TOKEN_TTL_SECONDS: "86400"
ARCHE_GATEWAY_BASE_URL: http://web:3000
ARCHE_PUBLIC_BASE_URL: https://${ARCHE_DOMAIN}
ARCHE_SESSION_TTL_DAYS: "7"
ARCHE_COOKIE_SECURE: "true"
ARCHE_SEED_ADMIN_EMAIL: ${ARCHE_SEED_ADMIN_EMAIL}
ARCHE_SEED_ADMIN_PASSWORD: ${ARCHE_SEED_ADMIN_PASSWORD}
ARCHE_SEED_ADMIN_SLUG: ${ARCHE_SEED_ADMIN_SLUG:-admin}
CONTAINER_PROXY_HOST: docker-socket-proxy
CONTAINER_PROXY_PORT: "2375"
OPENCODE_IMAGE: ${ARCHE_WORKSPACE_IMAGE:-ghcr.io/peaberry-studio/arche/workspace:latest}
OPENCODE_NETWORK: ${OPENCODE_NETWORK}
KB_CONTENT_HOST_PATH: /opt/arche/kb-content
KB_CONFIG_HOST_PATH: /opt/arche/kb-config
ARCHE_USERS_PATH: /opt/arche/users
expose:
- "3000"
volumes:
- /opt/arche/kb-content:/kb-content
- /opt/arche/kb-config:/kb-config
- /opt/arche/users:/opt/arche/users
postgres:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_DB: arche
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
docker-socket-proxy:
image: ghcr.io/tecnativa/docker-socket-proxy:master
restart: unless-stopped
environment:
CONTAINERS: 1
NETWORKS: 1
IMAGES: 1
INFO: 1
POST: 1
VOLUMES: 1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
volumes:
postgres_data:
-19
View File
@@ -1,19 +0,0 @@
{
"id": "arche",
"name": "Arche",
"version": "latest",
"description": "Arche is a self-hosted AI workspace platform that spawns per-user OpenCode coding containers backed by a shared Knowledge Base. Requires the `arche-internal` external Docker network and `/opt/arche/{kb-content,kb-config,users}` directories on the host before deploy. Images are linux/amd64 by default; override `ARCHE_WEB_IMAGE` and `ARCHE_WORKSPACE_IMAGE` to the `-arm64` tag variants for ARM64 hosts.",
"logo": "arche.svg",
"links": {
"github": "https://github.com/peaberry-studio/arche",
"website": "https://github.com/peaberry-studio/arche",
"docs": "https://github.com/peaberry-studio/arche#readme"
},
"tags": [
"ai",
"coding",
"workspace",
"self-hosted",
"llm"
]
}
-33
View File
@@ -1,33 +0,0 @@
[variables]
main_domain = "${domain}"
postgres_password = "${password:32}"
session_pepper = "${base64:32}"
encryption_key = "${base64:32}"
internal_token = "${base64:32}"
gateway_token_secret = "${base64:32}"
connector_oauth_state_secret = "${base64:32}"
seed_admin_email = "${email}"
seed_admin_password = "${password:16}"
[config]
env = [
"ARCHE_DOMAIN=${main_domain}",
"POSTGRES_PASSWORD=${postgres_password}",
"ARCHE_SESSION_PEPPER=${session_pepper}",
"ARCHE_ENCRYPTION_KEY=${encryption_key}",
"ARCHE_INTERNAL_TOKEN=${internal_token}",
"ARCHE_GATEWAY_TOKEN_SECRET=${gateway_token_secret}",
"ARCHE_CONNECTOR_OAUTH_STATE_SECRET=${connector_oauth_state_secret}",
"ARCHE_SEED_ADMIN_EMAIL=${seed_admin_email}",
"ARCHE_SEED_ADMIN_PASSWORD=${seed_admin_password}",
"ARCHE_SEED_ADMIN_SLUG=admin",
"ARCHE_WEB_IMAGE=ghcr.io/peaberry-studio/arche/web:latest",
"ARCHE_WORKSPACE_IMAGE=ghcr.io/peaberry-studio/arche/workspace:latest",
"OPENCODE_NETWORK=",
]
mounts = []
[[config.domains]]
serviceName = "web"
port = 3000
host = "${main_domain}"
-5
View File
@@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128">
<rect width="128" height="128" rx="28" fill="#101828"/>
<circle cx="64" cy="64" r="34" fill="#38bdf8" opacity="0.92"/>
<text x="64" y="74" text-anchor="middle" font-family="Arial, sans-serif" font-size="30" font-weight="700" fill="#ffffff">A</text>
</svg>

Before

Width:  |  Height:  |  Size: 325 B

-15
View File
@@ -1,15 +0,0 @@
services:
archivebox:
image: archivebox/archivebox:latest
restart: unless-stopped
command: server --quick-init 0.0.0.0:8000
environment:
ALLOWED_HOSTS: ${ALLOWED_HOSTS}
PUBLIC_INDEX: ${PUBLIC_INDEX}
PUBLIC_SNAPSHOTS: ${PUBLIC_SNAPSHOTS}
volumes:
- archivebox_data:/data
expose:
- "8000"
volumes:
archivebox_data:
-17
View File
@@ -1,17 +0,0 @@
{
"id": "archivebox",
"name": "ArchiveBox",
"version": "latest",
"description": "ArchiveBox is a self-hosted internet archiving solution for saving websites, media, and documents.",
"logo": "archivebox.svg",
"links": {
"github": "https://github.com/ArchiveBox/ArchiveBox",
"website": "https://archivebox.io/",
"docs": "https://docs.archivebox.io/"
},
"tags": [
"archive",
"bookmarks",
"web"
]
}
-15
View File
@@ -1,15 +0,0 @@
[variables]
main_domain = "${domain}"
[config]
mounts = []
[[config.domains]]
serviceName = "archivebox"
port = 8000
host = "${main_domain}"
[config.env]
ALLOWED_HOSTS = "*"
PUBLIC_INDEX = "True"
PUBLIC_SNAPSHOTS = "True"
+3 -2
View File
@@ -1,8 +1,9 @@
version: "3.8"
services:
argilla-web:
image: argilla/argilla-server:latest
restart: unless-stopped
expose:
ports:
- 6900
environment:
- ARGILLA_HOME_PATH=/var/lib/argilla
@@ -73,4 +74,4 @@ volumes:
argilladata: {}
elasticdata: {}
dbdata: {}
redisdata: {}
redisdata: {}
-17
View File
@@ -1,17 +0,0 @@
{
"id": "argilla",
"name": "Argilla",
"version": "latest",
"description": "Argilla is a robust platform designed to help engineers and data scientists streamline the management of machine learning data workflows. It simplifies tasks like data labeling, annotation, and quality control.",
"logo": "logo.svg",
"links": {
"github": "https://github.com/argilla-io/argilla",
"website": "https://www.argilla.io/",
"docs": "https://docs.argilla.io/"
},
"tags": [
"machine-learning",
"data-labeling",
"ai"
]
}
+3 -2
View File
@@ -1,8 +1,9 @@
version: "3.8"
services:
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:2.19.4
restart: unless-stopped
expose:
ports:
- 80
environment:
- TZ=UTC
@@ -13,4 +14,4 @@ services:
volumes:
config: {}
metadata: {}
metadata: {}
-17
View File
@@ -1,17 +0,0 @@
{
"id": "audiobookshelf",
"name": "Audiobookshelf",
"version": "2.19.4",
"description": "Audiobookshelf is a self-hosted server designed to manage and play your audiobooks and podcasts. It works best when you have an organized directory structure.",
"logo": "logo.png",
"links": {
"github": "https://github.com/advplyr/audiobookshelf",
"website": "https://www.audiobookshelf.org",
"docs": "https://www.audiobookshelf.org/docs"
},
"tags": [
"media",
"audiobooks",
"podcasts"
]
}
+3 -2
View File
@@ -16,8 +16,9 @@ services:
condition: service_healthy
postgres:
condition: service_healthy
expose:
ports:
- 9091
redis:
image: redis:7-alpine
restart: unless-stopped
@@ -51,4 +52,4 @@ services:
volumes:
authelia_config:
redis_data:
postgres_data:
postgres_data:

Some files were not shown because too many files have changed in this diff Show More