Compare commits

..
Author SHA1 Message Date
Mauricio Siu ccbe9e549c chore: ensure newline at end of file in build preview workflow
- Added a newline at the end of the build-preview.yml file to adhere to best practices for file formatting.
2025-03-16 12:20:10 -06:00
Mauricio Siu 003610de8e chore: remove pnpm caching from build preview workflow
- Eliminated the caching configuration for pnpm in the build preview workflow to streamline the dependency installation process.
2025-03-16 12:14:33 -06:00
Mauricio Siu cdf7613733 chore: fix indentation in build preview workflow
- Corrected the indentation of the build-preview job in the GitHub Actions workflow for improved readability and consistency.
2025-03-16 12:12:34 -06:00
Mauricio Siu 436cd4e702 chore: enable caching for pnpm in build preview workflow
- Added caching for pnpm to improve dependency installation speed in the build preview workflow.
2025-03-16 12:12:05 -06:00
Mauricio Siu 1dbefad6d4 chore: update pnpm version in build preview workflow
- Specified pnpm version 8 in the build preview workflow for consistency and compatibility.
2025-03-16 12:09:39 -06:00
Mauricio Siu 899a91a254 chore: refactor build preview workflow for improved clarity and efficiency
- Renamed job from "build-and-deploy" to "build-preview" for better clarity.
- Added concurrency settings to manage workflow runs effectively.
- Streamlined steps by removing unnecessary commands and comments.
- Updated pnpm action to the latest version and ensured proper Node.js versioning.
2025-03-16 12:07:43 -06:00
Mauricio Siu fe211b36fa chore: update Cloudflare deployment secrets in deploy preview workflow
- Changed environment variable names for Cloudflare API token and account ID to align with updated naming conventions.
- Ensured consistency in secret management for deployment to Cloudflare Pages.
2025-03-16 11:45:12 -06:00
Mauricio Siu e5ac8f96ba fix: correct workflow trigger syntax in build preview deployment
- Updated the workflow trigger from 'n' to 'on' for proper event handling.
- Ensured the workflow correctly responds to pull request events.
2025-03-16 11:39:17 -06:00
Mauricio Siu b5ae891d5d chore: update GitHub Actions workflows for build and deploy preview
- Renamed the build preview workflow for clarity.
- Removed unnecessary permissions and commented out deployment steps in the build workflow.
- Introduced a new deploy preview workflow that triggers on successful completion of the build workflow, handling artifact download and deployment to Cloudflare Pages.
2025-03-16 11:38:19 -06:00
Mauricio Siu a0085b44d0 Merge branch 'main' into onetimesecret 2025-03-16 11:33:04 -06:00
Nicholas Penree 95885291d2 feat: add One Time Secret template 2025-03-12 19:21:36 -04:00
1057 changed files with 6603 additions and 47612 deletions
-100
View File
@@ -1,100 +0,0 @@
name: Template Bug Report
description: Report a bug in one of our open source templates (Supabase, Appwrite, etc.)
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Before opening a new issue, please search existing issues to see if this bug has already been reported.
This template is specifically for bugs in our open source templates like Supabase, Appwrite, and others.
- type: input
attributes:
label: Template Name
description: Which template is experiencing the bug?
placeholder: "e.g: Supabase, Appwrite, Plausible, etc."
validations:
required: true
- type: textarea
attributes:
label: Relevant Logs of the Error
description: |
**IMPORTANT:** Go to the logs tab in your Dokploy dashboard and take a screenshot of the error.
Please be clear and include the full error message. You can also paste text logs here.
placeholder: |
Please attach a clear screenshot of the error logs from the logs tab.
If you're pasting text logs, include them here:
```
[Your error logs here]
```
validations:
required: true
- type: textarea
attributes:
label: Steps to Reproduce the Error
description: |
Please provide clear, detailed steps to reproduce this bug.
Be as specific as possible with numbered steps.
placeholder: |
1. Deploy the [Template Name] template
2. Configure the following settings: [be specific]
3. Navigate to [specific page/section]
4. Perform [specific action]
5. Error occurs at [specific point]
**Expected behavior:** What should happen
**Actual behavior:** What actually happens
validations:
required: true
- type: textarea
attributes:
label: Environment Information
description: Please provide information about your environment
render: bash
placeholder: |
Operating System: Ubuntu 20.04
Dokploy version: 0.2.2
VPS Provider: DigitalOcean, Hetzner, etc.
Template Version: [if known]
Browser: Chrome, Firefox, etc. [if relevant]
validations:
required: true
- type: dropdown
attributes:
label: When does this error occur?
options:
- "During template deployment"
- "After successful deployment"
- "When accessing the application"
- "During configuration"
- "Other"
validations:
required: true
- type: textarea
attributes:
label: Additional Context
description: Any additional information that might help us understand and fix this bug
placeholder: |
- Did this work before? When did it stop working?
- Have you made any custom modifications to the template?
- Any workarounds you've found?
validations:
required: false
- type: dropdown
attributes:
label: Will you send a PR to fix it?
description: Let us know if you are planning to submit a pull request to address this issue
options:
- "Yes"
- "No"
- "Maybe, need help"
validations:
required: true
-11
View File
@@ -1,11 +0,0 @@
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
@@ -1,59 +0,0 @@
name: New Template Request
description: Suggest a new template for the project
labels: ["template"]
body:
- type: input
attributes:
label: Template Name
description: Provide a clear and descriptive name for the template
placeholder: "e.g: Supabase, Appwrite, etc."
validations:
required: true
- type: input
attributes:
label: Template URL
description: Link to the repository or resource where the template is located
placeholder: "https://github.com/username/repo"
validations:
required: true
- type: input
attributes:
label: Docker Compose Link
description: Link to docker-compose.yml file or Docker documentation (optional) but would be helpful
placeholder: "https://github.com/username/repo/blob/main/docker-compose.yml"
validations:
required: false
- type: textarea
attributes:
label: Resources
description: List the resources, links, or any other information that would be helpful to know about the template
placeholder: |
- Link to the template
- Link to the documentation
- Link to the repository
- Link to the website
validations:
required: false
- type: textarea
attributes:
label: Template Description
description: Provide a detailed description of what this template does and when to use it
placeholder: |
This template is perfect for creating modern web applications with React and TypeScript...
validations:
required: false
- type: dropdown
attributes:
label: Will you send a PR to implement it?
description: Let us know if you are planning to submit a pull request to add this template
options:
- "Yes"
- "No"
- "Maybe, need help"
validations:
required: true
-69
View File
@@ -1,69 +0,0 @@
# Dokploy Open Source Templates - Copilot Instructions
## Project Overview
This repository maintains Docker Compose templates for deploying open-source applications via Dokploy, a self-hosted PaaS alternative to Heroku. The core structure revolves around the `blueprints/` directory, where each subdirectory represents a deployable service (e.g., `blueprints/ghost/` for Ghost blogging platform).
Key components:
- **Blueprints**: Self-contained templates with `docker-compose.yml` (service definitions) and `template.toml` (Dokploy-specific configuration for domains, env vars, mounts).
- **meta.json**: Centralized index of all templates, aggregated from blueprint metadata. Entries include `id`, `name`, `version`, `description`, `logo`, `links`, and `tags`.
- **Scripts**: Node.js tools in root and `build-scripts/` for maintaining `meta.json` (deduplication, sorting, validation).
Data flow: New templates added to `blueprints/` → Metadata updated in `meta.json` → Processing scripts ensure consistency → App builds include static blueprints/meta for preview.
The "why": Enables rapid, standardized deployment of 200+ OSS apps on Dokploy without manual config. Structure prioritizes simplicity—each blueprint is independent, no shared state or complex interdependencies.
## Key Files and Directories
- `meta.json`: Array of template objects. Always process after edits using `node dedupe-and-sort-meta.js` to remove duplicates (by `id`) and sort alphabetically.
- `blueprints/<service>/`:
- `docker-compose.yml`: Standard Docker Compose v3.8. Avoid `ports`, `container_name`, `networks`—Dokploy handles isolation via internal networks.
- `template.toml`: Defines variables (e.g., `${domain}`), domains (service:port → host), env vars, and mounts. Use helpers like `${password:32}`, `${uuid}`, `${jwt:secret_var}`.
- `logo.svg/png`: Service icon, referenced in `meta.json`.
- `dedupe-and-sort-meta.js`: Standalone script—reads `meta.json`, removes duplicate `id`s (keeps first), sorts by `id` (case-insensitive), creates timestamped backup.
- `build-scripts/process-meta.js`: Advanced processor with CLI options (`--verbose`, `--no-backup`, `--input`/`--output`), JSON schema validation (required: `id`, `name`, `version`, `description`, `links.github`, `logo`, `tags` array).
Exemplary blueprint: `blueprints/ghost/`—`docker-compose.yml` exposes port 2368; `template.toml` maps domain to Ghost service; meta entry tags as ["blogging", "cms"].
## Development Workflow
1. **Add/Update Template**:
- 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]`.
- Add/update `meta.json` entry with exact `id` matching folder.
- Run `node dedupe-and-sort-meta.js --backup` to validate/sort.
- Commit; PR triggers Dokploy preview (base64 import for testing).
2. **CI/CD**:
- `.github/workflows/validate-meta.yml` (if present): Runs validation on push/PR—fails on duplicates, invalid JSON, missing fields.
- Integrate processing: Add `npm run process-meta` to build steps; use `--no-backup` in CI.
No tests in repo—focus on manual validation via scripts and Dokploy deploys. Debug: Check console output from processing scripts for warnings (e.g., missing `id`).
## Conventions and Patterns
- **Template IDs**: Lowercase, kebab-case (e.g., `active-pieces`); unique across repo—enforced by dedupe script.
- **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}").
- 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`.
- **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.
## Integration Points
- **Dokploy**: Templates import via base64 (full compose + config) or URL. Test deploys validate env interpolation, domain proxying.
- **External Deps**: Docker Compose (v3.8+); TOML parsing via `@iarna/toml` in app. No runtime deps beyond Node/pnpm for dev.
- **PR Previews**: Auto-generated on GitHub—use for end-to-end testing without local Dokploy.
When editing, always re-run meta processing and validate blueprint deploy in preview.
-18
View File
@@ -1,18 +0,0 @@
## What is this PR about?
New PR of [Template Name]
## Checklist
Before submitting this PR, please make sure that:
- [ ] I have read the suggestions in the README.md file https://github.com/Dokploy/templates?tab=readme-ov-file#general-requirements-when-creating-a-template
- [ ] I have tested the template in my instance, so the maintainers don't spend time trying to figure out what's wrong.
- [ ] I have added tests that demonstrate that my correction works or that my new feature works.
## Issues related (if applicable)
Close automatically the related issues using the keywords: `closes #ISSUE_NUMBER`
## Screenshots or Videos
+9
View File
@@ -27,6 +27,15 @@ jobs:
- name: Build
working-directory: app
run: pnpm build
# - name: Deploy to Cloudflare Pages
# uses: AdrianGonz97/refined-cf-pages-action@v1
# with:
# apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
# accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
# githubToken: ${{ secrets.GITHUB_TOKEN }}
# projectName: ${{ secrets.CLOUDFLARE_PROJECT_NAME }}
# deploymentName: Preview
# directory: app/dist
- name: Upload build artifact
uses: actions/upload-artifact@v4
+1 -3
View File
@@ -5,9 +5,7 @@ on:
workflows: [Build Preview Deployment]
types:
- completed
pull_request:
branches:
- canary
permissions:
actions: read
deployments: write
@@ -1,317 +0,0 @@
name: Validate Docker Compose Files
on:
pull_request:
branches:
- canary
paths:
- 'blueprints/**/docker-compose.yml'
- 'blueprints/**/template.toml'
workflow_dispatch:
jobs:
validate-docker-compose:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Necesitamos el historial completo para comparar con base
- name: Set up Docker Compose
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
with:
node-version: 20.16.0
- name: Set up pnpm
uses: pnpm/action-setup@v4
with:
version: 8
- name: Install dependencies
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 }})
# 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)
# Crear lista de directorios únicos que tienen cambios
CHANGED_DIRS=$(echo -e "$CHANGED_COMPOSE\n$CHANGED_TOML" | sed 's|blueprints/\([^/]*\)/.*|\1|' | sort -u)
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 Docker Compose files syntax
id: validate-compose-syntax
run: |
echo "🔍 Validating Docker Compose files syntax..."
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
echo "$COMPOSE_FILES" | while read -r compose_file; do
if [ -z "$compose_file" ]; then
continue
fi
TEMPLATE_DIR=$(dirname "$compose_file")
TEMPLATE_NAME=$(basename "$TEMPLATE_DIR")
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 Validating syntax: $TEMPLATE_NAME"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# 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
else
echo "✅ docker-compose.yml syntax is valid"
fi
# 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 "❌ Docker Compose syntax validation failed"
exit 1
else
echo ""
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
-80
View File
@@ -1,80 +0,0 @@
name: Validate and Process Meta.json
on:
push:
branches: [canary]
paths: ["meta.json"]
pull_request:
branches: [canary]
paths: ["meta.json"]
workflow_dispatch:
jobs:
validate-meta:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "18"
- 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');
}
"
+4 -80
View File
@@ -3,10 +3,10 @@ name: Validate Blueprints Structure and Meta
on:
pull_request:
branches:
- canary
- main
push:
branches:
- canary
- main
jobs:
validate:
@@ -28,7 +28,7 @@ jobs:
TEMPLATE_NAME=$(basename "$dir")
COMPOSE_FILE="$dir/docker-compose.yml"
TEMPLATE_FILE="$dir/template.toml"
TEMPLATE_FILE="$dir/template.yml"
if [ ! -f "$COMPOSE_FILE" ]; then
echo "❌ Missing docker-compose.yml in $TEMPLATE_NAME"
@@ -36,7 +36,7 @@ jobs:
fi
if [ ! -f "$TEMPLATE_FILE" ]; then
echo "❌ Missing template.toml in $TEMPLATE_NAME"
echo "❌ Missing template.yml in $TEMPLATE_NAME"
ERROR=1
fi
fi
@@ -49,82 +49,6 @@ jobs:
echo "✅ Blueprint folders validated successfully."
fi
- name: Validate meta.json structure and required fields
run: |
echo "🔍 Validating meta.json structure and required fields..."
# 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..."
-1
View File
@@ -1 +0,0 @@
node_modules
-212
View File
@@ -1,212 +0,0 @@
# AGENTS.md: AI Collaboration Guide
This document provides essential context for AI models interacting with this project. Adhering to these guidelines will ensure consistency and maintain code quality.
## 1. Project Overview & Purpose
- **Primary Goal:** This is the official repository for Dokploy Open Source Templates, maintaining Docker Compose templates for deploying 200+ open-source applications via Dokploy (a self-hosted PaaS alternative to Heroku). The project enables rapid, standardized deployment of OSS applications without manual configuration.
- **Business Domain:** Self-hosted Platform as a Service (PaaS), DevOps tooling, containerized application deployment, and open-source software distribution.
## 2. Core Technologies & Stack
- **Languages:** JavaScript (Node.js), TypeScript (React frontend), YAML (Docker Compose), TOML (template configuration), JSON (metadata), Shell (build scripts).
- **Frameworks & Runtimes:** Node.js runtime, Vite 5.x (for React 19.x frontend), Docker Compose v3.8+, React Router 7.x.
- **Databases:** No direct database usage in core project (templates may include various databases like PostgreSQL, MySQL, Redis, etc.).
- **Key Libraries/Dependencies:**
- **Core:** `nodemon` for development, custom Node.js scripts for meta processing.
- **Frontend:** `fuse.js` (fuzzy search), `zustand` (state management), `@iarna/toml` (TOML parsing), `shadcn/ui` components, `@radix-ui` primitives, `@codemirror` (code editor), `react-router-dom`, `tailwindcss`.
- **Build:** `vite-plugin-static-copy` for copying blueprints to dist.
- **Platforms:** Linux (primary), Docker containers, Dokploy PaaS, Web browsers (for frontend preview), Cloudflare Pages (for PR previews).
- **Package Manager:** npm for root project, pnpm for frontend (`app/` directory).
## 3. Architectural Patterns
- **Overall Architecture:** Template collection and management system with decentralized blueprint architecture. Each blueprint is a self-contained Docker Compose application with Dokploy-specific configuration.
- **Directory Structure Philosophy:**
- `/blueprints`: Contains all deployable application templates, each as a subdirectory with `docker-compose.yml`, `template.toml`, and logo files. Each blueprint is independent with no shared state.
- `/app`: Vite + React + TypeScript frontend for local preview and development.
- `/src/components`: React components (UI components in `/ui` subdirectory using shadcn/ui).
- `/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`: 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: 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
- **Formatting:** JavaScript follows standard conventions with 2-space indentation. TypeScript in frontend uses ESLint configuration. YAML uses 2-space indentation. TOML files use standard formatting.
- **Naming Conventions:**
- Variables, functions: camelCase (`myVariable`, `myFunction`)
- 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: 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.
- **Frontend:** Component-based React architecture with hooks for logic separation. Zustand for centralized state management. Custom hooks encapsulate complex logic (e.g., `useFuseSearch` for search functionality).
- **Common Patterns & Idioms:**
- **Metaprogramming:** Minimal use of advanced JavaScript features in scripts, focusing on simple, maintainable code.
- **Memory Management:** Relies on Node.js garbage collection and React's automatic memory management.
- **Polymorphism:** Uses JavaScript prototype-based objects and functional programming patterns. React components use composition over inheritance.
- **Type Safety:**
- Backend scripts: JavaScript with JSDoc comments for documentation.
- Frontend: TypeScript with strict type checking enabled.
- **Concurrency:**
- Backend: Synchronous processing model for meta.json operations.
- Frontend: React's concurrent rendering, `useDeferredValue` for debouncing, `useEffect` for async operations.
- **State Management:** Zustand store pattern with selectors for optimal re-renders. URL params synced with search state via React Router.
- **Error Handling:**
- Backend: Node.js error-first callback pattern and try-catch blocks. Scripts validate JSON structure and fail fast on errors.
- Frontend: Error boundaries for React components, try-catch for async operations, user-friendly error messages via toast notifications.
## 5. Key Files & Entrypoints
- **Main Entrypoints:**
- **Backend:** `dedupe-and-sort-meta.js` - Primary script for processing meta.json file.
- **Frontend:** `app/src/main.tsx` - React application entry point.
- **Configuration:**
- `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 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.
## 6. Development & Testing Workflow
- **Local Development Environment:**
- **Install dependencies**
```bash
# Root project
npm install
# Frontend (uses pnpm)
cd app && pnpm install
```
- **Process meta.json** (CRITICAL: Run after ANY meta.json edits)
```bash
npm run process-meta
# or
make process-meta
# or
node dedupe-and-sort-meta.js
```
- **Validate without modifying**
```bash
npm run validate-meta
# or
make validate
```
- **Quick check for duplicates/sort status**
```bash
make check
```
- **Clean backup files**
```bash
make clean
```
- **Task Configuration:**
- **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 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 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.
## 7. Specific Instructions for AI Collaboration
- **Contribution Guidelines:**
- Follow existing Dokploy template structure strictly. Each blueprint must be independent with no shared state.
- **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 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
- **Example:**
```yaml
version: "3.8"
services:
ghost:
image: ghost:6-alpine
restart: always
volumes:
- ghost:/var/lib/ghost/content
volumes:
ghost:
```
- **template.toml Conventions:**
- **Variables:** Define in `[variables]` section, use helpers for secrets
- **Domains:** `[[config.domains]]` with `serviceName`, `port`, `host` (path optional)
- **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}`
- **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
- **Logo:** Filename only (e.g., `"ghost.jpeg"`), file must exist in blueprint folder
- **Frontend Development:**
- Use TypeScript with strict type checking
- Follow React hooks patterns, avoid class components
- Use Zustand selectors for state access to optimize re-renders
- Fuse.js searches across `name`, `description`, `tags`, `id` fields
- Use shadcn/ui components for consistency
- Sync URL params with search state via React Router
- **Security:**
- Be mindful of security when handling template configurations
- NEVER hardcode secrets in templates - use Dokploy's variable system with helpers
- Pin Docker images to specific versions to avoid supply chain attacks
- Validate user input in frontend before processing
- **Dependencies:**
- 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
- **Commit Messages:** Follow conventional commit patterns (e.g., `feat:`, `fix:`, `docs:`, `chore:`).
- **Common Pitfalls to Avoid:**
1. Forgetting to process meta.json after editing (CI will fail)
2. Template ID mismatch between meta.json and folder name
3. Including `ports`, `container_name`, or `networks` in docker-compose.yml
4. Using object syntax for env vars in template.toml (must be array of strings)
5. Logo file missing or filename mismatch in meta.json
6. Version mismatch between meta.json and docker-compose.yml image tag
-208
View File
@@ -1,208 +0,0 @@
# Contributing to Dokploy Open Source Templates
Thank you for your interest in contributing to the Dokploy Open Source Templates repository! This project maintains a collection of Docker Compose templates for easy deployment via Dokploy. Contributions help expand and improve the available templates for the community.
We welcome contributions in the form of new templates, improvements to existing ones, bug fixes, documentation updates, and more. Please follow the guidelines below to ensure a smooth review and integration process.
## Code of Conduct
This project adheres to the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). By participating, you are expected to uphold this code. Reports of unacceptable behavior can be directed to the project maintainers.
## How to Contribute
1. **Fork the Repository**: Start by forking this repository to your GitHub account.
2. **Create a New Branch**: From your fork, create a feature branch named descriptively (e.g., `add-grafana-template`).
3. **Make Your Changes**: Follow the specific guidelines for the type of contribution (e.g., adding a template).
4. **Commit and Push**: Commit your changes with clear, concise messages. Push the branch to your fork.
5. **Open a Pull Request (PR)**: Create a PR from your branch to the main repository. Reference any related issues in the PR description.
6. **Automated Preview**: Every PR automatically deploys a preview of your template to Dokploy. You can test it before merging (see "Testing Your Contribution" below).
7. **Review and Merge**: Respond to feedback from maintainers. Once approved, your changes will be merged.
For larger changes or questions, open an issue first to discuss your ideas.
## Adding a New Template
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
restart: unless-stopped
volumes:
- grafana-storage:/var/lib/grafana
volumes:
grafana-storage: {}
```
3. **Add `template.toml`**: Configure domains, environment variables, mounts, and variables. This file is crucial for Dokploy integration.
- Sections: `[variables]` (optional), `[config]` with `[[config.domains]]`, `[[config.env]]`, `[[config.mounts]]`.
- Use `${variable}` syntax to reference variables or helpers (e.g., `${domain}`, `${password:32}`).
- Match `serviceName` in domains to the service name in `docker-compose.yml`.
- Example for Grafana:
```toml
[variables]
main_domain = "${domain}"
[config]
[[config.domains]]
serviceName = "grafana"
port = 3000
host = "${main_domain}"
[[config.mounts]]
filePath = "/content/file.txt"
content = """
My content
"""
```
4. **Update `meta.json`**: Add an entry for your template in the root `meta.json`. Include:
- `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).
- `tags`: Array of categories (e.g., ["monitoring"]).
- Example:
```json
{
"id": "grafana",
"name": "Grafana",
"version": "9.5.20",
"description": "Grafana is an open source platform for data visualization and monitoring.",
"logo": "grafana.svg",
"links": {
"github": "https://github.com/grafana/grafana",
"website": "https://grafana.com/",
"docs": "https://grafana.com/docs/"
},
"tags": ["monitoring"]
}
```
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 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.
- The preview URL (auto-generated).
- Any testing notes.
## Template.toml Structure
- **[variables]**: Define reusable values or use helpers.
Example:
```toml
[variables]
main_domain = "${domain}"
my_password = "${password:32}"
```
- **[config.domains]**: Map services to domains/ports.
- Required: `serviceName`, `port`, `host`.
- Optional: `path`.
- **[config.env]**: Array of environment variables (strings).
Example: `env = ["GF_SECURITY_ADMIN_PASSWORD=${password:32}"]`
- **[config.mounts]**: Inline files or configs.
- `filePath`: Destination in container.
- `content`: Multi-line string.
### Helpers
Use these in `${}` for dynamic values:
- `${domain}`: Random subdomain.
- `${password:length}`: Random password (default 32 chars).
- `${base64:length}`: Base64-encoded random string.
- `${hash:length}`: Random hash.
- `${uuid}`: UUID.
- `${randomPort}`: Random port.
- `${email}`: Random email.
- `${username}`: Random lowercase username.
- `${timestamp}`: Current timestamp (ms).
- `${timestamps:datetime}` or `${timestampms:datetime}`: Timestamp at specific date (e.g., `${timestamps:2030-01-01T00:00:00Z}`).
- `${jwt:secret_var:payload_var}`: JWT token (advanced; see README for details).
## Best Practices and Suggestions
- **Docker Compose**:
- Omit explicit ports; let Dokploy handle exposure.
- Use persistent volumes for data (e.g., databases).
- **Template.toml**:
- Keep service names consistent across files.
- Use variables/helpers for secrets (e.g., passwords, API keys).
- Minimize env vars; only include essentials.
- **General**:
- Templates should be self-contained and deployable out-of-the-box.
- Use official/latest stable images.
- Avoid hardcoding domains, networks, or secrets.
- Ensure compatibility with Dokploy's isolated deployments.
- **Logo and Metadata**:
- Logos should be vector (SVG preferred) or high-res PNG.
- Descriptions: 1-2 sentences, SEO-friendly.
- Tags: Relevant categories (e.g., "database", "web", "monitoring").
## Testing Your Contribution
Before submitting a PR:
1. Run the project locally:
```bash
cd app
pnpm install
pnpm run dev
# Visit http://localhost:5173/
```
2. Use the PR preview:
- After pushing, check the PR for the auto-generated preview URL.
- Search for your template, copy the Base64 config.
- In your Dokploy instance: Create a Compose service > Advanced > Import > Paste Base64 > Deploy.
- Verify the service starts and is accessible (e.g., via assigned domain).
3. Test edge cases: Restart, volume persistence, env vars.
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 `meta.json` and update `docker-compose.yml` image tags.
- Test thoroughly, as changes may affect users.
## Reporting Bugs or Requesting Features
- Open an issue with:
- Clear title and description.
- Steps to reproduce (for bugs).
- Template details (for feature requests).
- Use the issue templates in `.github/ISSUE_TEMPLATE/`.
## Questions?
If you're unsure about anything, open a discussion or issue. We're here to help!
Happy contributing! 🚀
+46 -126
View File
@@ -7,14 +7,14 @@ 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`)
3. Add the template to the `blueprints` folder (docker-compose.yml, template.yml)
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)
8. Every PR will automatically deploy a preview of the template to Dokploy.
9. if anyone want to test the template before merging it, you can enter to the preview URL in the PR description, and search the template, click on the Template Card, scroll down and then copy the BASE64 value, and paste in the advanced section of your compose service, in the Import section or optional you can use the preview URL and paste in the
BASE URL when creating a template.
BASE URL when creating a template.
#### Optional
@@ -45,24 +45,21 @@ services:
volumes:
grafana-storage: {}
```
3. Add the `template.toml` file to the folder, this is where we specify the domains, mounts and env variables, to understand more the structure of `template.toml` you can read here [Template.toml structure](#template.toml-structure)
3. Add the `template.yml` file to the folder, this is where we specify the domains, mounts and env variables, to understand more the structure of `template.yml` you can read here [Template.yml structure](#templateyml-structure)
```toml
[variables]
main_domain = "${domain}"
```yaml
variables:
main_domain: ${domain}
[config]
[[config.domains]]
serviceName = "grafana"
port = 3000
host = "${main_domain}"
[config.env]
[[config.mounts]]
config:
domains:
- serviceName: grafana
port: 3000
host: ${main_domain}
env: []
mounts: []
```
4. Add meta information to the `meta.json` file in the root folder
4. Add the `meta.json` file to the folder
```json
{
@@ -85,144 +82,67 @@ host = "${main_domain}"
6. Commit and push your changes
7. Create a pull request
### Template.toml structure
### Template.yml structure
Dokploy use a defined structure for the `template.yml` file, we have 4 sections available:
Dokploy use a defined structure for the `template.toml` file, we have 4 sections available:
1. `variables`: This is where we define the variables that will be used in the `domains`, `env` and `mounts` sections.
2. `domains`: This is where we define the configuration for the template.
3. `env`: This is where we define the environment variables for the template.
4. `mounts`: This is where we define the mounts for the template.
- The `variables(Optional)` structure is the following:
```toml
[variables]
main_domain = "${domain}"
my_domain = "https://my-domain.com"
my_password = "${password:32}"
any_helper = "${you-can-use-any-helper}"
```yaml
variables:
main_domain: ${domain}
my_domain: https://my-domain.com
my_password: ${password:32}
any_helper: ${you-can-use-any-helper}
```
- The `config` structure is the following:
```toml
[config]
# Optional sections below
```yaml
config:
domains: # Optional
- serviceName: grafana # Required
port: 3000 # Required
host: ${main_domain} # Required
path: / # Optional
[[config.domains]]
serviceName = "grafana" # Required
port = 3000 # Required
host = "${main_domain}" # Required
path = "/" # Optional
env: # Optional
- AP_HOST=${main_domain}
- AP_API_KEY=${api_key}
- AP_ENCRYPTION_KEY=${encryption_key}
- AP_JWT_SECRET=${jwt_secret}
- AP_POSTGRES_PASSWORD=${postgres_password}
env = [
"AP_HOST=${main_domain}",
"AP_API_KEY=${api_key}",
"AP_ENCRYPTION_KEY=${encryption_key}",
"AP_JWT_SECRET=${jwt_secret}",
"AP_POSTGRES_PASSWORD=${postgres_password}"
]
[[config.mounts]]
filePath = "/content/file.txt"
content = """
My content
"""
mounts: # Optional or []
- filePath: /content/file.txt
content: |
My content
```
Important: you can reference any variable in the `domains`, `env` and `mounts` sections. just use the `${variable_name}` syntax, in the case you don't want to define a variable, you can use the `domain`, `base64`, `password`, `hash`, `uuid`, `randomPort`, `timestamp`, `jwt`, `email`, or `username` helpers.
Important: you can reference any variable in the `domains`, `env` and `mounts` sections. just use the `${variable_name}` syntax, in the case you don't want to define a variable, you can use the `domain`, `base64`, `password`, `hash`, `uuid`, `randomPort` or `timestamp` helpers.
### Helpers
We have a few helpers that are very common when creating a template, these are:
- `domain`: This is a helper that will generate a random domain for the template.
- `base64 or base64:length`: This is a helper that will encode a string to base64 (lenght is the number of bytes to encode not the encoded string length).
- `base64 or base64:length`: This is a helper that will encode a string to base64.
- `password or password:length`: This is a helper that will generate a random password for the template.
- `hash or hash:length`: This is a helper that will generate a hash for the template
- `hash or hash:length`: This is a helper that will generate a hash for the template.
- `uuid`: This is a helper that will generate a uuid for the template.
- `randomPort`: This is a helper that will generate a random port for the template.
- `email`: This is a helper that will generate a random email for the template.
- `username`: This is a helper that will generate a random username in lowercase for the template.
- `timestamp`: This is a helper that will generate a timestamp for "now" in milli-second.
- `timestampms or timestampms:datetime`: This is a helper that will generate a timestamp in milli-seconds.
- `timestamps or timestamps:datetime`: This is a helper that will generate a timestamp in seconds.
- `datetime` parameter for `timestamps/timestampms` helpers must be a valid value for javascript new Date() (ie: `timestamps:2030-01-01T00:00:00Z`)
- `jwt`: This is a helper that will generate a jwt for the template.
- `jwt:length`: will generate a random hex string of bytes length. _This should not be used in newer templates_
- `jwt:secret_var_name`: will generate a jwt with some default values, secret var name should be the name of the variable holding the secret
- `jwt:secret_var_name:payload_var_name`: is the same as above but you can pass partial or full payload for the jwt.
Here's a full example
```toml
[variables]
main_domain = "${domain}"
mysecret = "cQsdycq1hDLopQonF6jUTqgQc5WEZTwWLL02J6XJ"
mypayload = """
{
"role": "jwt-tester",
"iss": "dokploy-templates",
"exp": ${timestamps:2030-01-01T00:00:00Z}
}
"""
jwt = "${jwt:mysecret:mypayload}"
```
- `timestamp`: This is a helper that will generate a timestamp.
- `jwt or jwt:length`: This is a helper that will generate a jwt for the template.
## General Requirements when creating a template
- Don't use this way in your docker compose file:
```yaml
services:
grafana:
image: grafana/grafana-enterprise:9.5.20
restart: unless-stopped
ports:
- 3000:3000
# Instead use this way:
ports:
- 3000
```
- Don't use this way in your template.toml file, make sure to use the same service name as the one in the docker compose file:
```toml
[config]
[[config.domains]]
serviceName = "MyGrafanaService"
# Instead use this way:
serviceName = "grafana" # Make sure to use the same service name as the one in the docker compose file
```
- Don't use container_name in your docker compose file, make sure to use the same service name as the one in the template.toml file:
```yaml
services:
grafana:
container_name: grafana # ❌ Remove this
```
- Don't use dokploy-network in your docker compose file, by default all the templates have this flag enabled https://docs.dokploy.com/docs/core/docker-compose/utilities#isolated-deployments, so by default they have a internal network created, so you don't need to create a new one or use the dokploy-network name.
```yaml
services:
grafana:
networks:
- dokploy-network # ❌ Remove this or any other network defined
```
- Please before submit a PR, make sure to test the template in your instance, so the maintainers don't spend time trying to figure out what's wrong.
1. Everytime you submit a PR, it will display a Preview Link.
2. Enter to the Preview Link and search the template you've submitted.
3. Click on the Template Card, and click the Copy Button in the Base64 Configuration.
4. Go to your instance, create a new Compose Service, go to Advanced Section -> Scroll Down -> Import Section -> Paste the Base64 Value -> Click on the Import Button
5. If everything is correct and set, you should see a modal with all the details (Compose File, Environment Variables, Mounts, Domains, etc)
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.
use the command `node dedupe-and-sort-meta.js` to deduplicate and sort the meta.json file.
+27 -30
View File
@@ -10,46 +10,43 @@
"preview": "vite preview"
},
"dependencies": {
"@codemirror/autocomplete": "^6.19.1",
"@codemirror/lang-json": "^6.0.2",
"@codemirror/lang-yaml": "^6.1.2",
"@codemirror/language": "^6.11.3",
"@codemirror/autocomplete": "^6.18.6",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-yaml": "^6.1.1",
"@codemirror/language": "^6.10.1",
"@codemirror/legacy-modes": "6.4.0",
"@codemirror/view": "6.29.0",
"@iarna/toml": "^2.2.5",
"@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tabs": "^1.1.13",
"@tailwindcss/vite": "^4.1.16",
"@uiw/codemirror-theme-github": "^4.25.2",
"@uiw/react-codemirror": "^4.25.2",
"@radix-ui/react-dialog": "^1.1.6",
"@radix-ui/react-dropdown-menu": "^2.1.6",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-popover": "^1.1.6",
"@radix-ui/react-slot": "^1.1.2",
"@radix-ui/react-tabs": "^1.1.3",
"@tailwindcss/vite": "^4.0.12",
"@uiw/codemirror-theme-github": "^4.22.1",
"@uiw/react-codemirror": "^4.22.1",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "1.0.0",
"copy-to-clipboard": "^3.3.3",
"lucide-react": "^0.479.0",
"next-themes": "^0.4.6",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"react-router-dom": "^7.9.4",
"sonner": "^2.0.7",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.16",
"next-themes": "^0.4.5",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"sonner": "^2.0.1",
"tailwind-merge": "^3.0.2",
"tailwindcss": "^4.0.12",
"tailwindcss-animate": "^1.0.7",
"vite-plugin-static-copy": "^2.3.2",
"yaml": "2.7.1",
"zustand": "^5.0.8"
"vite-plugin-static-copy": "2.3.0",
"zustand": "^5.0.3"
},
"devDependencies": {
"@types/node": "^20.19.23",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@vitejs/plugin-react": "^4.7.0",
"@types/node": "^20.8.2",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
"globals": "^15.15.0",
"typescript": "~5.7.3",
"vite": "^6.4.1"
"typescript": "~5.7.2",
"vite": "^6.2.0"
}
}
+767 -1009
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -1 +0,0 @@
../../blueprints
-36
View File
@@ -1,36 +0,0 @@
import yaml from "yaml";
import toml from "@iarna/toml";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
function convertYamlToToml(yamlContent) {
const parsedYaml = yaml.parse(yamlContent);
return toml.stringify(parsedYaml);
}
function processDirectory(dirPath) {
const files = fs.readdirSync(dirPath);
files.forEach((file) => {
const filePath = path.join(dirPath, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
processDirectory(filePath);
} else if (file === "template.yml") {
console.log(`Converting ${filePath}`);
const yamlContent = fs.readFileSync(filePath, "utf8");
const tomlContent = convertYamlToToml(yamlContent);
const tomlPath = path.join(dirPath, "template.toml");
fs.writeFileSync(tomlPath, tomlContent);
}
});
}
// Ruta al directorio blueprints relativa al script
const blueprintsPath = path.join(__dirname, "..", "blueprints");
processDirectory(blueprintsPath);
+5 -8
View File
@@ -3,19 +3,16 @@ import Navigation from "./components/Navigation";
import Search from "./components/Search";
import { useStore } from "@/store";
import "./App.css";
import { BrowserRouter } from "react-router-dom";
function App() {
const view = useStore((state) => state.view);
return (
<BrowserRouter>
<div className="min-h-screen">
<Navigation />
<Search />
<TemplateGrid view={view} />
</div>
</BrowserRouter>
<div className="min-h-screen">
<Navigation />
<Search />
<TemplateGrid view={view} />
</div>
);
}
+1 -1
View File
@@ -32,7 +32,7 @@ const Navigation = () => {
<Button
onClick={() => {
window.open(
"https://github.com/Dokploy/templates/issues/new",
"https://github.com/Dokploy/dokploy/discussions/new?category=ideas",
"_blank"
);
}}
+3 -53
View File
@@ -16,10 +16,9 @@ import { Check, ChevronsUpDown } from "lucide-react";
import React from "react";
import { Tabs, TabsList, TabsTrigger } from "./ui/tabs";
import SelectedTags from "./SelectedTags";
import { useSearchParams } from "react-router-dom";
const Search = () => {
const { templates, searchQuery, setSearchQuery, setView, templatesCount, setFilteredTemplates, setTemplatesCount } =
const { templates, searchQuery, setSearchQuery, setView, templatesCount } =
useStore();
const selectedTags = useStore((state) => state.selectedTags);
const addSelectedTag = useStore((state) => state.addSelectedTag);
@@ -27,7 +26,6 @@ const Search = () => {
const [open, setOpen] = React.useState(false);
const [tagSearch, setTagSearch] = React.useState("");
const view = useStore((state) => state.view);
const [searchParams, setSearchParams] = useSearchParams();
// Get all unique tags, safely handle empty templates
const uniqueTags = React.useMemo(() => {
@@ -45,54 +43,6 @@ const Search = () => {
);
}, [uniqueTags, tagSearch]);
// Initialize search query from URL params and apply filters
React.useEffect(() => {
const queryFromUrl = searchParams.get("q") || "";
if (queryFromUrl !== searchQuery) {
setSearchQuery(queryFromUrl);
}
// Apply filters whenever templates, search query or selected tags change
if (templates) {
const filtered = templates.filter((template) => {
// Filter by search query - search across name and description
const searchTerm = queryFromUrl.toLowerCase();
const matchesSearch =
template.name.toLowerCase().includes(searchTerm) ||
template.description.toLowerCase().includes(searchTerm);
// Filter by selected tags
const matchesTags =
selectedTags.length === 0 ||
selectedTags.every((tag) => template.tags.includes(tag));
return matchesSearch && matchesTags;
});
setFilteredTemplates(filtered);
setTemplatesCount(filtered.length);
}
}, [searchParams, templates, selectedTags, setSearchQuery, setFilteredTemplates, setTemplatesCount]);
// Update URL params when search query changes
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newQuery = e.target.value;
setSearchQuery(newQuery);
if (newQuery) {
setSearchParams({ q: newQuery });
} else {
searchParams.delete("q");
setSearchParams(searchParams);
}
};
// Clear search and URL params
const handleClearSearch = () => {
setSearchQuery("");
searchParams.delete("q");
setSearchParams(searchParams);
};
return (
<div className=" mx-auto p-4 lg:p-12 border-b w-full">
{/* <h1 className="text-2xl md:text-3xl xl:text-4xl font-bold text-center mb-8">
@@ -113,11 +63,11 @@ const Search = () => {
type="text"
placeholder="Search templates..."
value={searchQuery}
onChange={handleSearchChange}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full p-6"
/>
{searchQuery.length > 0 ? (
<div className="cursor-pointer" onClick={handleClearSearch}>
<div className="cursor-pointer" onClick={() => setSearchQuery("")}>
<XIcon className="absolute end-3 translate-y-3.5 top-1/2 h-5 w-5 text-gray-400" />
</div>
) : (
+2 -4
View File
@@ -55,9 +55,7 @@ const TemplateDialog: React.FC<TemplateDialogProps> = ({
config: templateFiles.config || "",
};
// Use encodeURIComponent to handle Unicode characters properly
const jsonString = JSON.stringify(configObj, null, 2);
return btoa(unescape(encodeURIComponent(jsonString)));
return btoa(JSON.stringify(configObj, null, 2));
};
return (
@@ -230,7 +228,7 @@ const TemplateDialog: React.FC<TemplateDialogProps> = ({
<div className="relative w-full rounded-md overflow-hidden border">
<CodeEditor
value={templateFiles.config || ""}
language="toml"
language="yaml"
className="font-mono w-full [&_*]:!break-words"
/>
<Button
+2 -1
View File
@@ -82,7 +82,7 @@ const TemplateGrid: React.FC<TemplateGridProps> = ({ view }) => {
try {
const [dockerComposeRes, configRes] = await Promise.all([
fetch(`/blueprints/${templateId}/docker-compose.yml`),
fetch(`/blueprints/${templateId}/template.toml`),
fetch(`/blueprints/${templateId}/template.yml`),
]);
const dockerCompose = dockerComposeRes.ok
@@ -120,6 +120,7 @@ const TemplateGrid: React.FC<TemplateGridProps> = ({ view }) => {
return matchesSearch && matchesTags;
});
console.log("ffiltered tem", filtered.length);
setTemplatesCount(filtered.length);
setFilteredTemplates(filtered);
}, [searchQuery, selectedTags]);
+1 -1
View File
@@ -121,7 +121,7 @@ function dockerComposeComplete(
interface Props extends ReactCodeMirrorProps {
wrapperClassName?: string;
disabled?: boolean;
language?: "yaml" | "json" | "properties" | "shell" | "toml";
language?: "yaml" | "json" | "properties" | "shell";
lineWrapping?: boolean;
lineNumbers?: boolean;
}
-24
View File
@@ -1,24 +0,0 @@
version: "3"
services:
ackee:
image: electerious/ackee:3.4.2
ports:
- "3000"
environment:
- ACKEE_USERNAME=${ACKEE_USERNAME}
- ACKEE_PASSWORD=${ACKEE_PASSWORD}
- ACKEE_MONGODB=${ACKEE_MONGODB}
mongo:
image: mongo:4
environment:
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD}
- MONGO_INITDB_ROOT_USERNAME=mongo
volumes:
- mongo-data:/data/db
volumes:
mongo-data:
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

-19
View File
@@ -1,19 +0,0 @@
[variables]
ACKEE_USERNAME = "default"
ACKEE_PASSWORD = "${password:16}"
MONGO_INITDB_ROOT_PASSWORD = "${password:16}"
ACKEE_MONGODB = "mongodb://mongo:${MONGO_INITDB_ROOT_PASSWORD}@mongo:27017"
[config]
[[config.domains]]
serviceName = "ackee"
port = 3000
host = "${domain}"
[config.env]
ACKEE_USERNAME = "${ACKEE_USERNAME}"
ACKEE_PASSWORD = "${ACKEE_PASSWORD}"
ACKEE_MONGODB = "${ACKEE_MONGODB}"
MONGO_INITDB_ROOT_PASSWORD = "${MONGO_INITDB_ROOT_PASSWORD}"
-21
View File
@@ -1,21 +0,0 @@
[variables]
main_domain = "${domain}"
api_key = "${password:32}"
encryption_key = "${password:32}"
jwt_secret = "${password:32}"
postgres_password = "${password:32}"
[config]
env = [
"AP_HOST=${main_domain}",
"AP_API_KEY=${api_key}",
"AP_ENCRYPTION_KEY=${encryption_key}",
"AP_JWT_SECRET=${jwt_secret}",
"AP_POSTGRES_PASSWORD=${postgres_password}",
]
mounts = []
[[config.domains]]
serviceName = "activepieces"
port = 80
host = "${main_domain}"
+21
View File
@@ -0,0 +1,21 @@
variables:
main_domain: ${domain}
api_key: ${password:32}
encryption_key: ${password:32}
jwt_secret: ${password:32}
postgres_password: ${password:32}
config:
domains:
- serviceName: activepieces
port: 80
host: ${main_domain}
env:
- AP_HOST=${main_domain}
- AP_API_KEY=${api_key}
- AP_ENCRYPTION_KEY=${encryption_key}
- AP_JWT_SECRET=${jwt_secret}
- AP_POSTGRES_PASSWORD=${postgres_password}
mounts: []
-11
View File
@@ -1,11 +0,0 @@
[variables]
main_domain = "${domain}"
[config]
env = []
mounts = []
[[config.domains]]
serviceName = "actualbudget"
port = 5_006
host = "${main_domain}"
+12
View File
@@ -0,0 +1,12 @@
variables:
main_domain: ${domain}
config:
domains:
- serviceName: actualbudget
port: 5006
host: ${main_domain}
env: []
mounts: []
-20
View File
@@ -1,20 +0,0 @@
version: "3.8"
services:
adguardhome:
image: adguard/adguardhome:latest
restart: unless-stopped
ports:
- "53:53/tcp" # DNS
- "53:53/udp" # DNS
- "67:67/udp" # DHCP Server
- "68:68/tcp" # DHCP Client
- "853:853/tcp" # DNS over TLS, DNS-over-QUIC
- "853:853/udp" # DNS over TLS, DNS-over-QUIC
- "6060:6060/tcp" # HTTP (pprof)
volumes:
- adguardhome-work:/opt/adguardhome/work
- adguardhome-conf:/opt/adguardhome/conf
volumes:
adguardhome-work: {}
adguardhome-conf: {}
-20
View File
@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="80px" height="80px" viewBox="0 0 80 80" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>logo@2x</title>
<desc>Created with Sketch.</desc>
<g id="logo" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Rectangle" fill="#FFFFFF" x="0" y="0" width="80" height="80" rx="20"></rect>
<g id="Group-10" transform="translate(12.000000, 12.000000)">
<g id="Group-9">
<g id="Group-8">
<g id="Group-7">
<path d="M28.2221276,0 C19.4007575,0 8.75997994,2.07442553 8.65485005e-06,6.64038298 C8.65485005e-06,16.501617 -0.120909272,41.0689362 28.2221276,57.855 C56.5657909,41.0689362 56.4454995,16.501617 56.4454995,6.64038298 C47.6849017,2.07442553 37.0441241,0 28.2221276,0 L28.2221276,0 Z" id="Path" fill="#68BC71"></path>
<path d="M28.1932991,57.8379179 C-0.120827266,41.0522735 8.65485006e-06,16.4982725 8.65485006e-06,6.64038298 C8.75043947,2.07939831 19.3775821,0.00452145957 28.1932991,7.38217799e-06 L28.1932991,57.8379237 Z" id="Combined-Shape" fill="#67B279"></path>
</g>
<path d="M27.1926958,38.6027397 L44.2590846,15.6010416 C43.0084943,14.5986526 41.911548,15.3061181 41.3076915,15.8538333 L41.2856573,15.8555888 L27.0557264,30.6585285 L21.6942672,24.2064902 C19.1365123,21.2514028 15.6592758,23.5054616 14.8469876,24.1011604 L27.1926958,38.6027397" id="Fill-11" fill="#FFFFFF"></path>
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

-12
View File
@@ -1,12 +0,0 @@
[variables]
main_domain = "${domain}"
[config]
env = []
mounts = []
[[config.domains]]
serviceName = "adguardhome"
port = 3000
host = "${main_domain}"
-7
View File
@@ -1,7 +0,0 @@
version: "3.8"
services:
adminer:
image: adminer:4.8.1
restart: unless-stopped
ports:
- 8080
-42
View File
@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="791px"
height="792px" viewBox="-91.5 -0.5 791 792" enable-background="new -91.5 -0.5 791 792" xml:space="preserve">
<g id="Layer_2">
<rect x="-91.5" y="-0.5" fill="#005982" width="791" height="792"/>
<rect x="-90.5" y="-0.5" fill="none" width="790" height="792"/>
</g>
<g id="Layer_1">
<path fill="#FFFFFF" d="M336.162,343.266c-7.003-27.503-58.849-54.543-149.432-54.543c-90.517,0-142.358,27.004-149.416,54.488
c-0.543,1.058-0.875,2.241-0.875,3.513v249.82c0,2.393,1.101,4.505,2.794,5.937c11.992,25.549,62.822,49.176,147.496,49.176
c84.697,0,135.532-23.639,147.507-49.199c1.679-1.428,2.768-3.531,2.768-5.913v-5.691c0-0.047,0.008-0.09,0.008-0.137
c0-0.027-0.008-0.05-0.008-0.077v-81.224c0-0.046,0.008-0.09,0.008-0.136c0-0.028-0.008-0.051-0.008-0.078V429.61
c0-0.048,0.008-0.09,0.008-0.137c0-0.027-0.008-0.052-0.008-0.079v-79.59c0-0.047,0.008-0.09,0.008-0.137s-0.008-0.094-0.008-0.141
v-2.802C337.005,345.475,336.685,344.312,336.162,343.266z M321.392,509.372c-0.165,21.415-55.417,45.233-134.662,45.233
c-79.36,0-134.668-23.889-134.668-45.33c0-0.027-0.008-0.052-0.008-0.079v-52.169c22.261,18.717,67.967,33.387,134.676,33.387
c66.698,0,112.392-14.665,134.662-33.375V509.372z M321.392,429.567C321.227,450.98,265.974,474.8,186.73,474.8
c-79.36,0-134.668-23.889-134.668-45.331c0-0.026-0.008-0.051-0.008-0.078v-52.169c22.261,18.716,67.967,33.386,134.676,33.386
c66.698,0,112.392-14.661,134.662-33.374V429.567z M186.73,304.337c79.244,0,134.497,23.818,134.662,45.233v0.191
c-0.165,21.415-55.417,45.233-134.662,45.233c-79.36,0-134.668-23.889-134.668-45.327
C52.062,328.229,107.37,304.337,186.73,304.337z M186.73,636.043c-79.36,0-134.668-23.89-134.668-45.327
c0-0.027-0.008-0.05-0.008-0.077v-53.802c22.261,18.717,67.967,33.387,134.676,33.387c66.698,0,112.392-14.665,134.662-33.375
v53.961C321.227,612.228,265.974,636.043,186.73,636.043z"/>
<g>
<path fill="#FF0000" d="M427.38,365.951l-71.116-59.932l71.116-59.827l16.46,16.671l-52.335,42.944l52.335,43.472L427.38,365.951z
"/>
<path fill="#FF0000" d="M566.131,260.227c0,5.909-0.897,11.203-2.691,15.88c-1.793,4.679-4.362,8.723-7.702,12.134
c-3.342,3.413-7.369,6.173-12.081,8.283c-4.714,2.11-9.989,3.553-15.827,4.326l-1.056,19.837h-22.052l-1.899-39.673h15.3
c3.445,0,6.418-0.491,8.915-1.477c2.496-0.984,4.537-2.322,6.12-4.01s2.743-3.64,3.482-5.856c0.738-2.216,1.107-4.554,1.107-7.017
c0-3.376-0.722-6.524-2.163-9.444c-1.442-2.918-3.692-5.486-6.753-7.702c-3.06-2.216-6.947-3.957-11.659-5.223
c-4.713-1.266-10.377-1.899-16.987-1.899h-6.226v-23.952h7.28c10.832,0,20.293,1.284,28.384,3.851
c8.088,2.568,14.841,5.979,20.258,10.235c5.416,4.257,9.479,9.127,12.188,14.614C564.775,248.62,566.131,254.317,566.131,260.227z
M533.21,349.069c0,2.321-0.458,4.502-1.371,6.542c-0.915,2.041-2.147,3.835-3.693,5.381c-1.548,1.548-3.377,2.76-5.486,3.64
c-2.11,0.879-4.363,1.319-6.753,1.319c-2.393,0-4.627-0.44-6.7-1.319c-2.076-0.88-3.888-2.092-5.435-3.64
c-1.548-1.546-2.779-3.34-3.692-5.381c-0.915-2.04-1.372-4.221-1.372-6.542s0.457-4.5,1.372-6.542
c0.913-2.04,2.145-3.833,3.692-5.381c1.547-1.546,3.358-2.76,5.435-3.64c2.073-0.879,4.308-1.319,6.7-1.319
c2.39,0,4.643,0.44,6.753,1.319c2.109,0.88,3.938,2.094,5.486,3.64c1.546,1.548,2.778,3.342,3.693,5.381
C532.752,344.568,533.21,346.748,533.21,349.069z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.6 KiB

-8
View File
@@ -1,8 +0,0 @@
[variables]
main_domain = "${domain}"
[config]
[[config.domains]]
serviceName = "adminer"
port = 8080
host = "${main_domain}"
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.9 KiB

@@ -1,51 +0,0 @@
version: "3.8"
services:
web:
image: ghcr.io/seanmorley15/adventurelog-frontend:latest
restart: unless-stopped
depends_on:
- server
environment:
# Frontend needs to know where the backend is
PUBLIC_SERVER_URL: ${PUBLIC_SERVER_URL}
ORIGIN: ${ORIGIN}
BODY_SIZE_LIMIT: ${BODY_SIZE_LIMIT}
db:
image: postgis/postgis:16-3.5
restart: unless-stopped
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
server:
image: ghcr.io/seanmorley15/adventurelog-backend:latest
restart: unless-stopped
depends_on:
- db
volumes:
- adventurelog_media:/code/media/
environment:
# DB settings for Django
PGHOST: ${PGHOST}
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
# Django app settings
SECRET_KEY: ${SECRET_KEY}
DJANGO_ADMIN_USERNAME: ${DJANGO_ADMIN_USERNAME}
DJANGO_ADMIN_PASSWORD: ${DJANGO_ADMIN_PASSWORD}
DJANGO_ADMIN_EMAIL: ${DJANGO_ADMIN_EMAIL}
DEBUG: ${DEBUG}
CSRF_TRUSTED_ORIGINS: ${CSRF_TRUSTED_ORIGINS}
FRONTEND_URL: ${FRONTEND_URL}
PUBLIC_URL: ${PUBLIC_URL}
volumes:
postgres_data: {}
adventurelog_media: {}
-44
View File
@@ -1,44 +0,0 @@
[variables]
frontend_domain = "${domain}"
backend_domain = "${domain}"
postgres_password = "${password:16}"
secret_key = "${password:32}"
admin_username = "admin"
admin_password = "${password:12}"
admin_email = "${email}"
[config]
# Frontend domain
[[config.domains]]
serviceName = "web"
port = 3000
host = "${frontend_domain}"
# Backend domain
[[config.domains]]
serviceName = "server"
port = 80
host = "${backend_domain}"
[config.env]
# --- Postgres (values used by both db and server services)
PGHOST = "db"
POSTGRES_DB = "adventure"
POSTGRES_USER = "adventure"
POSTGRES_PASSWORD = "${postgres_password}"
# --- Django Backend
SECRET_KEY = "${secret_key}"
DJANGO_ADMIN_USERNAME = "${admin_username}"
DJANGO_ADMIN_PASSWORD = "${admin_password}"
DJANGO_ADMIN_EMAIL = "${admin_email}"
DEBUG = "False"
CSRF_TRUSTED_ORIGINS = "http://${backend_domain},http://${frontend_domain}"
FRONTEND_URL = "http://${frontend_domain}"
PUBLIC_URL = "http://${backend_domain}"
# --- Frontend
PUBLIC_SERVER_URL = "${backend_domain}"
ORIGIN = "http://${frontend_domain}"
BODY_SIZE_LIMIT = "Infinity"
-65
View File
@@ -1,65 +0,0 @@
version: "3.8"
services:
affinepro:
image: ghcr.io/toeverything/affine-graphql:stable-780dd83
restart: unless-stopped
ports:
- 3010
volumes:
- affine-storage:/root/.affine/storage
- affine-config:/root/.affine/config
environment:
- REDIS_SERVER_HOST=redis
- REDIS_SERVER_PASSWORD=${REDIS_PASSWORD}
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/affinepro
- AFFINE_SERVER_HOST=${DOMAIN}
- MAILER_HOST=${MAILER_HOST}
- MAILER_PORT=${MAILER_PORT}
- MAILER_USER=${MAILER_USER}
- MAILER_PASSWORD=${MAILER_PASSWORD}
- MAILER_SENDER=${MAILER_SENDER}
depends_on:
- db
- redis
migration:
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_SERVER_HOST=${DOMAIN}
- MAILER_HOST=${MAILER_HOST}
- MAILER_PORT=${MAILER_PORT}
- MAILER_USER=${MAILER_USER}
- MAILER_PASSWORD=${MAILER_PASSWORD}
- MAILER_SENDER=${MAILER_SENDER}
volumes:
- affine-storage:/root/.affine/storage
- affine-config:/root/.affine/config
depends_on:
- db
- redis
db:
image: postgres:15-alpine
restart: unless-stopped
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=affinepro
volumes:
- postgres-data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis-data:/data
volumes:
affine-storage: {}
affine-config: {}
postgres-data: {}
redis-data: {}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

-25
View File
@@ -1,25 +0,0 @@
[variables]
main_domain = "${domain}"
postgres_password = "${password:16}"
redis_password = "${password:16}"
mailer_host = ""
mailer_port = "587"
mailer_user = ""
mailer_password = ""
mailer_sender = ""
[config]
[[config.domains]]
serviceName = "affinepro"
port = 3010
host = "${main_domain}"
[config.env]
DOMAIN = "${main_domain}"
POSTGRES_PASSWORD = "${postgres_password}"
REDIS_PASSWORD = "${redis_password}"
MAILER_HOST = "${mailer_host}"
MAILER_PORT = "${mailer_port}"
MAILER_USER = "${mailer_user}"
MAILER_PASSWORD = "${mailer_password}"
MAILER_SENDER = "${mailer_sender}"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

-22
View File
@@ -1,22 +0,0 @@
services:
agentdvr:
image: mekayelanik/ispyagentdvr:latest
restart: unless-stopped
environment:
- PUID=1000
- PGID=1000
- TZ=${TIMEZONE:-America/New_York}
- AGENTDVR_WEBUI_PORT=8090
volumes:
- agentdvr-config:/AgentDVR/Media/XML/
- agentdvr-media:/AgentDVR/Media/WebServerRoot/Media/
- agentdvr-commands:/AgentDVR/Commands/
ports:
- 8090
- 3478/udp
- 50000-50100/udp
volumes:
agentdvr-config: {}
agentdvr-media: {}
agentdvr-commands: {}
-14
View File
@@ -1,14 +0,0 @@
[variables]
main_domain = "${domain}"
timezone = "America/New_York"
[config]
[[config.domains]]
serviceName = "agentdvr"
port = 8090
host = "${main_domain}"
[config.env]
TIMEZONE = "${timezone}"
[[config.mounts]]
-55
View File
@@ -1,55 +0,0 @@
version: "3.8"
services:
akaunting:
image: docker.io/akaunting/akaunting:latest
restart: unless-stopped
depends_on:
akaunting-db:
condition: service_healthy
environment:
# App config
APP_URL: ${APP_URL}
LOCALE: ${LOCALE}
# Database connection
DB_HOST: akaunting-db
DB_PORT: 3306
DB_NAME: ${DB_NAME}
DB_USERNAME: ${DB_USERNAME}
DB_PASSWORD: ${DB_PASSWORD}
DB_PREFIX: ${DB_PREFIX}
# First-run bootstrap (company + admin)
COMPANY_NAME: ${COMPANY_NAME}
COMPANY_EMAIL: ${COMPANY_EMAIL}
ADMIN_EMAIL: ${ADMIN_EMAIL}
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
# Expose only to internal network; domain routing handled by Dokploy/Traefik
expose:
- "80"
volumes:
- akaunting-data:/var/www/html
akaunting-db:
image: mariadb:10.11
restart: unless-stopped
environment:
MYSQL_DATABASE: ${MYSQL_DATABASE}
MYSQL_USER: ${MYSQL_USER}
MYSQL_PASSWORD: ${MYSQL_PASSWORD}
# Randomizes root password on each deployment (recommended)
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
volumes:
- akaunting-db:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
volumes:
akaunting-data: {}
akaunting-db: {}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

-54
View File
@@ -1,54 +0,0 @@
[variables]
# Domain & UI
main_domain = "${domain}"
locale = "en-US"
# DB credentials (safe defaults; user can override in UI)
db_name = "akaunting"
db_user = "admin"
mysql_password = "${password:24}"
# Akaunting setup variables
company_name = "My Company"
company_email = "${email}"
admin_email = "${email}"
admin_password = "${password:24}"
# Akaunting table prefix: 3 hex chars + underscore (e.g., "a1f_")
db_prefix = "${hash:3}_"
# Domain routing for the web UI
[[config.domains]]
serviceName = "akaunting"
port = 80
host = "${main_domain}"
[config]
env = [
# App
"APP_URL=${main_domain}",
"LOCALE=${locale}",
# Database (app)
"DB_HOST=akaunting-db",
"DB_PORT=3306",
"DB_NAME=${db_name}",
"DB_USERNAME=${db_user}",
"DB_PASSWORD=${mysql_password}",
"DB_PREFIX=${db_prefix}",
# First-run bootstrap (app)
"COMPANY_NAME=${company_name}",
"COMPANY_EMAIL=${company_email}",
"ADMIN_EMAIL=${admin_email}",
"ADMIN_PASSWORD=${admin_password}",
# Database (server)
"MYSQL_DATABASE=${db_name}",
"MYSQL_USER=${db_user}",
"MYSQL_PASSWORD=${mysql_password}",
"MYSQL_RANDOM_ROOT_PASSWORD=yes"
]
# No custom mounts needed; volumes are already defined in compose.
[[config.mounts]]
-11
View File
@@ -1,11 +0,0 @@
[variables]
main_domain = "${domain}"
[config]
env = []
mounts = []
[[config.domains]]
serviceName = "alist"
port = 5_244
host = "${main_domain}"
+12
View File
@@ -0,0 +1,12 @@
variables:
main_domain: ${domain}
config:
domains:
- serviceName: alist
port: 5244
host: ${main_domain}
env: []
mounts: []
-12
View File
@@ -1,12 +0,0 @@
version: "3.8"
services:
alltube:
image: dnomd343/alltube:latest
restart: unless-stopped
ports:
- 80
environment:
- TITLE=${TITLE}
- CONVERT=${CONVERT}
- STREAM=${STREAM}
- REMUX=${REMUX}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

-18
View File
@@ -1,18 +0,0 @@
[variables]
main_domain = "${domain}"
title = "My AllTube Site"
convert = "true"
stream = "true"
remux = "true"
[config]
[[config.domains]]
serviceName = "alltube"
port = 80
host = "${main_domain}"
[config.env]
TITLE = "${title}"
CONVERT = "${convert}"
STREAM = "${stream}"
REMUX = "${remux}"
-17
View File
@@ -1,17 +0,0 @@
version: "3.8"
services:
ampache:
image: ampache/ampache:latest
restart: unless-stopped
ports:
- 80
volumes:
- config:/var/www/config
- log:/var/log/ampache
- mysql:/var/lib/mysql
- ${MEDIA_PATH}:/media
volumes:
config: {}
log: {}
mysql: {}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

-12
View File
@@ -1,12 +0,0 @@
[variables]
main_domain = "${domain}"
media_path = "/path/to/your/media"
[config]
[[config.domains]]
serviceName = "ampache"
port = 80
host = "${main_domain}"
[config.env]
MEDIA_PATH = "${media_path}"
-16
View File
@@ -1,16 +0,0 @@
version: "3.8"
services:
anonupload:
image: ghcr.io/supernova3339/anonfiles:1
restart: unless-stopped
ports:
- 80
environment:
- ADMIN_EMAIL=${ADMIN_EMAIL}
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
- CONTACT_EMAIL=${CONTACT_EMAIL}
volumes:
- uploads:/var/www/html/uploads
volumes:
uploads: {}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 KiB

-16
View File
@@ -1,16 +0,0 @@
[variables]
main_domain = "${domain}"
admin_email = "${email}"
admin_password = "${password:16}"
contact_email = "${email}"
[config]
[[config.domains]]
serviceName = "anonupload"
port = 80
host = "${main_domain}"
[config.env]
ADMIN_EMAIL = "${admin_email}"
ADMIN_PASSWORD = "${admin_password}"
CONTACT_EMAIL = "${contact_email}"
-15
View File
@@ -1,15 +0,0 @@
version: "3.8"
services:
anse-demo:
image: ddiu8081/anse:latest
restart: always
expose:
- 3000
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- OPENAI_API_BASE_URL=${OPENAI_API_BASE_URL}
- HEAD_SCRIPTS=${HEAD_SCRIPTS}
- SECRET_KEY=${SECRET_KEY}
- SITE_PASSWORD=${SITE_PASSWORD}
- OPENAI_API_MODEL=${OPENAI_API_MODEL}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

-18
View File
@@ -1,18 +0,0 @@
[variables]
main_domain = "${domain}"
secret_key = "${password:32}"
site_password = "${password:16}"
[config]
[[config.domains]]
serviceName = "anse-demo"
port = 3000
host = "${main_domain}"
[config.env]
OPENAI_API_KEY = ""
OPENAI_API_BASE_URL = "https://api.openai.com"
HEAD_SCRIPTS = ""
SECRET_KEY = "${secret_key}"
SITE_PASSWORD = "${site_password}"
OPENAI_API_MODEL = "gpt-4o-mini"
-12
View File
@@ -1,12 +0,0 @@
[variables]
main_domain = "${domain}"
service_hash = "${hash:32}"
[config]
env = ["ANSWER_HOST=http://${main_domain}", "SERVICE_HASH=${service_hash}"]
mounts = []
[[config.domains]]
serviceName = "answer"
port = 9_080
host = "${main_domain}"
+15
View File
@@ -0,0 +1,15 @@
variables:
main_domain: ${domain}
service_hash: ${hash:32}
config:
domains:
- serviceName: answer
port: 9080
host: ${main_domain}
env:
- ANSWER_HOST=http://${main_domain}
- SERVICE_HASH=${service_hash}
mounts: []
Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

-38
View File
@@ -1,38 +0,0 @@
version: "3.8"
services:
anubis:
image: ghcr.io/techarohq/anubis:latest
restart: unless-stopped
ports:
- "8923" # Anubis default port
environment:
# Required: Point to your frontend service locally
# Example: http://mydocker-wyeud:8080
- TARGET=${TARGET}
# Bind address
- BIND=:8923
# Cookie domain (set to your domain)
- COOKIE_DOMAIN=${COOKIE_DOMAIN}
# Challenge difficulty (1-20, default is 5)
# Lower = easier for users, Higher = harder for bots
- DIFFICULTY=${DIFFICULTY}
# OpenGraph passthrough (allows social media previews to work)
- OG_PASSTHROUGH=${OG_PASSTHROUGH}
- OG_EXPIRY_TIME=${OG_EXPIRY_TIME}
- OG_CACHE_CONSIDER_HOST=${OG_CACHE_CONSIDER_HOST}
# Optional: Serve robots.txt to discourage indexing
- SERVE_ROBOTS_TXT=${SERVE_ROBOTS_TXT} # Set to true if you don't want search engines
# Unconmment in case you want to connect the anubis container to dokploy-network
# networks:
# - dokploy-network
# networks:
# dokploy-network:
# external: true # Using existing dokploy-network to connect to your frontend locally
-17
View File
@@ -1,17 +0,0 @@
[variables]
main_domain = "${domain}"
[config.env]
COOKIE_DOMAIN = "${main_domain}"
TARGET = "YOURLOCALHOST:PORT"
DIFFICULTY = "5"
OG_PASSTHROUGH = true
OG_EXPIRY_TIME = "1h"
OG_CACHE_CONSIDER_HOST = true
SERVE_ROBOTS_TXT = false
mounts = []
[[config.domains]]
serviceName = "anubis"
port = 8923
host = "${main_domain}"
-16
View File
@@ -1,16 +0,0 @@
version: "3.8"
services:
anythingllm:
image: mintplexlabs/anythingllm:latest
restart: unless-stopped
ports:
- 3001
environment:
- STORAGE_DIR=/app/server/storage
volumes:
- storage:/app/server/storage
cap_add:
- SYS_ADMIN
volumes:
storage: {}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

-8
View File
@@ -1,8 +0,0 @@
[variables]
main_domain = "${domain}"
[config]
[[config.domains]]
serviceName = "anythingllm"
port = 3001
host = "${main_domain}"
-19
View File
@@ -1,19 +0,0 @@
# Example: Any-Sync-Bundle with embedded MongoDB and Redis (all-in-one image)
#
# Usage:
# docker compose -f compose.aio.yml up -d
#
# The bundle image already contains MongoDB and Redis. Only the bundle service is required
services:
any-sync-bundle:
image: ghcr.io/grishy/any-sync-bundle:latest
restart: unless-stopped
ports:
- "33010:33010"
- "33020:33020/udp"
volumes:
- ./data:/data
environment:
# Advertise addresses clients should use. Replace with your server hostname/IP.
ANY_SYNC_BUNDLE_INIT_EXTERNAL_ADDRS: "192.168.100.9"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

-4
View File
@@ -1,4 +0,0 @@
[config]
domains = []
mounts = []
env = []
Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

-257
View File
@@ -1,257 +0,0 @@
services:
nginx:
restart: on-failure
image: nginx
volumes:
- ../files/nginx/nginx.conf:/etc/nginx/nginx.conf
minio:
restart: on-failure
image: minio/minio
environment:
- MINIO_BROWSER_REDIRECT_URL=${APPFLOWY_BASE_URL}/minio
- MINIO_ROOT_USER=${APPFLOWY_S3_ACCESS_KEY:-minioadmin}
- MINIO_ROOT_PASSWORD=${APPFLOWY_S3_SECRET_KEY:-minioadmin}
command: server /data --console-address ":9001"
volumes:
- minio_data:/data
postgres:
restart: on-failure
image: pgvector/pgvector:pg16
environment:
- POSTGRES_USER=${POSTGRES_USER:-postgres}
- POSTGRES_DB=${POSTGRES_DB:-postgres}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
- POSTGRES_HOST=${POSTGRES_HOST:-postgres}
healthcheck:
test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER}", "-d", "${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 12
volumes:
- postgres_data:/var/lib/postgresql/data
- ./files/volumes/postgres/:/docker-entrypoint-initdb.d/
redis:
restart: on-failure
image: redis
gotrue:
restart: on-failure
image: appflowyinc/gotrue:${GOTRUE_VERSION:-latest}
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "--fail", "http://127.0.0.1:9999/health"]
interval: 5s
timeout: 5s
retries: 12
environment:
# Admin Configuration
- GOTRUE_ADMIN_EMAIL=${GOTRUE_ADMIN_EMAIL}
- GOTRUE_ADMIN_PASSWORD=${GOTRUE_ADMIN_PASSWORD}
- GOTRUE_DISABLE_SIGNUP=${GOTRUE_DISABLE_SIGNUP:-false}
# Site Configuration
- GOTRUE_SITE_URL=appflowy-flutter://
- GOTRUE_URI_ALLOW_LIST=${GOTRUE_URI_ALLOW_LIST:-**}
- API_EXTERNAL_URL=${API_EXTERNAL_URL}
# JWT Configuration
- GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET}
- GOTRUE_JWT_EXP=${GOTRUE_JWT_EXP:-7200}
- GOTRUE_JWT_ADMIN_GROUP_NAME=supabase_admin
# Database Configuration
- GOTRUE_DB_DRIVER=postgres
- DATABASE_URL=${GOTRUE_DATABASE_URL}
- PORT=9999
# Email Configuration
- GOTRUE_SMTP_HOST=${GOTRUE_SMTP_HOST}
- GOTRUE_SMTP_PORT=${GOTRUE_SMTP_PORT}
- GOTRUE_SMTP_USER=${GOTRUE_SMTP_USER}
- GOTRUE_SMTP_PASS=${GOTRUE_SMTP_PASS}
- GOTRUE_SMTP_ADMIN_EMAIL=${GOTRUE_SMTP_ADMIN_EMAIL}
- GOTRUE_SMTP_MAX_FREQUENCY=${GOTRUE_SMTP_MAX_FREQUENCY:-1ns}
- GOTRUE_RATE_LIMIT_EMAIL_SENT=${GOTRUE_RATE_LIMIT_EMAIL_SENT:-100}
- GOTRUE_MAILER_AUTOCONFIRM=${GOTRUE_MAILER_AUTOCONFIRM:-true}
# Email Templates
- GOTRUE_MAILER_URLPATHS_CONFIRMATION=/gotrue/verify
- GOTRUE_MAILER_URLPATHS_INVITE=/gotrue/verify
- GOTRUE_MAILER_URLPATHS_RECOVERY=/gotrue/verify
- GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE=/gotrue/verify
- GOTRUE_MAILER_TEMPLATES_MAGIC_LINK=${GOTRUE_MAILER_TEMPLATES_MAGIC_LINK}
# OAuth Providers
- GOTRUE_EXTERNAL_GOOGLE_ENABLED=${GOTRUE_EXTERNAL_GOOGLE_ENABLED:-false}
- GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID=${GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID}
- GOTRUE_EXTERNAL_GOOGLE_SECRET=${GOTRUE_EXTERNAL_GOOGLE_SECRET}
- GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI=${GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI}
- GOTRUE_EXTERNAL_GITHUB_ENABLED=${GOTRUE_EXTERNAL_GITHUB_ENABLED:-false}
- GOTRUE_EXTERNAL_GITHUB_CLIENT_ID=${GOTRUE_EXTERNAL_GITHUB_CLIENT_ID}
- GOTRUE_EXTERNAL_GITHUB_SECRET=${GOTRUE_EXTERNAL_GITHUB_SECRET}
- GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI=${GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI}
- GOTRUE_EXTERNAL_DISCORD_ENABLED=${GOTRUE_EXTERNAL_DISCORD_ENABLED:-false}
- GOTRUE_EXTERNAL_DISCORD_CLIENT_ID=${GOTRUE_EXTERNAL_DISCORD_CLIENT_ID}
- GOTRUE_EXTERNAL_DISCORD_SECRET=${GOTRUE_EXTERNAL_DISCORD_SECRET}
- GOTRUE_EXTERNAL_DISCORD_REDIRECT_URI=${GOTRUE_EXTERNAL_DISCORD_REDIRECT_URI}
# SAML Configuration
- GOTRUE_SAML_ENABLED=${GOTRUE_SAML_ENABLED:-false}
- GOTRUE_SAML_PRIVATE_KEY=${GOTRUE_SAML_PRIVATE_KEY}
appflowy_cloud:
restart: on-failure
image: appflowyinc/appflowy_cloud:${APPFLOWY_CLOUD_VERSION:-latest}
depends_on:
gotrue:
condition: service_healthy
environment:
# Core Configuration
- RUST_LOG=${RUST_LOG:-info}
- APPFLOWY_ENVIRONMENT=production
- APPFLOWY_DATABASE_URL=${APPFLOWY_DATABASE_URL}
- APPFLOWY_REDIS_URI=${APPFLOWY_REDIS_URI}
- APPFLOWY_WEB_URL=${APPFLOWY_WEB_URL}
# Authentication Configuration
- APPFLOWY_GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET}
- APPFLOWY_GOTRUE_JWT_EXP=${GOTRUE_JWT_EXP:-7200}
- APPFLOWY_GOTRUE_BASE_URL=${APPFLOWY_GOTRUE_BASE_URL}
# File Storage Configuration
- APPFLOWY_S3_CREATE_BUCKET=${APPFLOWY_S3_CREATE_BUCKET:-true}
- APPFLOWY_S3_USE_MINIO=${APPFLOWY_S3_USE_MINIO:-true}
- APPFLOWY_S3_MINIO_URL=${APPFLOWY_S3_MINIO_URL}
- APPFLOWY_S3_ACCESS_KEY=${APPFLOWY_S3_ACCESS_KEY}
- APPFLOWY_S3_SECRET_KEY=${APPFLOWY_S3_SECRET_KEY}
- APPFLOWY_S3_BUCKET=${APPFLOWY_S3_BUCKET:-appflowy}
- APPFLOWY_S3_REGION=${APPFLOWY_S3_REGION:-us-east-1}
- APPFLOWY_S3_PRESIGNED_URL_ENDPOINT=${APPFLOWY_S3_PRESIGNED_URL_ENDPOINT}
# Email Configuration
- APPFLOWY_MAILER_SMTP_HOST=${APPFLOWY_MAILER_SMTP_HOST}
- APPFLOWY_MAILER_SMTP_PORT=${APPFLOWY_MAILER_SMTP_PORT}
- APPFLOWY_MAILER_SMTP_USERNAME=${APPFLOWY_MAILER_SMTP_USERNAME}
- APPFLOWY_MAILER_SMTP_EMAIL=${APPFLOWY_MAILER_SMTP_EMAIL}
- APPFLOWY_MAILER_SMTP_PASSWORD=${APPFLOWY_MAILER_SMTP_PASSWORD}
- APPFLOWY_MAILER_SMTP_TLS_KIND=${APPFLOWY_MAILER_SMTP_TLS_KIND:-wrapper}
# Access Control and Performance
- APPFLOWY_ACCESS_CONTROL=${APPFLOWY_ACCESS_CONTROL:-true}
- APPFLOWY_DATABASE_MAX_CONNECTIONS=${APPFLOWY_DATABASE_MAX_CONNECTIONS:-40}
- APPFLOWY_WEBSOCKET_MAILBOX_SIZE=${APPFLOWY_WEBSOCKET_MAILBOX_SIZE:-6000}
# AI Configuration
- AI_SERVER_HOST=${AI_SERVER_HOST:-ai}
- AI_SERVER_PORT=${AI_SERVER_PORT:-5001}
- AI_OPENAI_API_KEY=${AI_OPENAI_API_KEY}
admin_frontend:
restart: on-failure
image: appflowyinc/admin_frontend:${APPFLOWY_ADMIN_FRONTEND_VERSION:-latest}
depends_on:
gotrue:
condition: service_healthy
appflowy_cloud:
condition: service_started
environment:
- RUST_LOG=${RUST_LOG:-info}
- ADMIN_FRONTEND_REDIS_URL=${ADMIN_FRONTEND_REDIS_URL}
- ADMIN_FRONTEND_GOTRUE_URL=${ADMIN_FRONTEND_GOTRUE_URL}
- ADMIN_FRONTEND_APPFLOWY_CLOUD_URL=${ADMIN_FRONTEND_APPFLOWY_CLOUD_URL}
- ADMIN_FRONTEND_PATH_PREFIX=${ADMIN_FRONTEND_PATH_PREFIX:-/console}
ai:
restart: on-failure
image: appflowyinc/appflowy_ai:${APPFLOWY_AI_VERSION:-latest}
depends_on:
postgres:
condition: service_healthy
appflowy_cloud:
condition: service_started
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5001/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
environment:
# Core AI Configuration
- AI_SERVER_PORT=${AI_SERVER_PORT:-5001}
- OPENAI_API_KEY=${AI_OPENAI_API_KEY}
- DEFAULT_AI_MODEL=${DEFAULT_AI_MODEL:-gpt-4o-mini}
- DEFAULT_AI_COMPLETION_MODEL=${DEFAULT_AI_COMPLETION_MODEL:-gpt-4o-mini}
# Azure OpenAI (optional)
- AZURE_OPENAI_API_KEY=${AZURE_OPENAI_API_KEY}
- AZURE_OPENAI_ENDPOINT=${AZURE_OPENAI_ENDPOINT}
- AZURE_OPENAI_API_VERSION=${AZURE_OPENAI_API_VERSION}
# Database and Cache
- AI_DATABASE_URL=${APPFLOWY_DATABASE_URL}
- AI_REDIS_URL=${APPFLOWY_REDIS_URI}
# File Storage for AI
- APPFLOWY_S3_ACCESS_KEY=${APPFLOWY_S3_ACCESS_KEY}
- APPFLOWY_S3_SECRET_KEY=${APPFLOWY_S3_SECRET_KEY}
- APPFLOWY_S3_BUCKET=${APPFLOWY_S3_BUCKET:-appflowy}
- APPFLOWY_S3_REGION=${APPFLOWY_S3_REGION:-us-east-1}
- AI_USE_MINIO=${APPFLOWY_S3_USE_MINIO:-true}
- AI_MINIO_URL=${APPFLOWY_S3_MINIO_URL}
# Integration
- AI_APPFLOWY_HOST=${APPFLOWY_BASE_URL}
- APPFLOWY_GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET}
appflowy_worker:
restart: on-failure
image: appflowyinc/appflowy_worker:${APPFLOWY_WORKER_VERSION:-latest}
depends_on:
postgres:
condition: service_healthy
environment:
# Core Configuration
- RUST_LOG=${RUST_LOG:-info}
- APPFLOWY_ENVIRONMENT=production
- APPFLOWY_WORKER_REDIS_URL=${APPFLOWY_WORKER_REDIS_URL}
- APPFLOWY_WORKER_ENVIRONMENT=production
- APPFLOWY_WORKER_DATABASE_URL=${APPFLOWY_WORKER_DATABASE_URL}
- APPFLOWY_WORKER_DATABASE_NAME=${APPFLOWY_WORKER_DATABASE_NAME}
- APPFLOWY_WORKER_IMPORT_TICK_INTERVAL=${APPFLOWY_WORKER_IMPORT_TICK_INTERVAL:-30}
# File Storage Configuration
- APPFLOWY_S3_USE_MINIO=${APPFLOWY_S3_USE_MINIO:-true}
- APPFLOWY_S3_MINIO_URL=${APPFLOWY_S3_MINIO_URL}
- APPFLOWY_S3_ACCESS_KEY=${APPFLOWY_S3_ACCESS_KEY}
- APPFLOWY_S3_SECRET_KEY=${APPFLOWY_S3_SECRET_KEY}
- APPFLOWY_S3_BUCKET=${APPFLOWY_S3_BUCKET:-appflowy}
- APPFLOWY_S3_REGION=${APPFLOWY_S3_REGION:-us-east-1}
# Email Configuration
- APPFLOWY_MAILER_SMTP_HOST=${APPFLOWY_MAILER_SMTP_HOST}
- APPFLOWY_MAILER_SMTP_PORT=${APPFLOWY_MAILER_SMTP_PORT}
- APPFLOWY_MAILER_SMTP_USERNAME=${APPFLOWY_MAILER_SMTP_USERNAME}
- APPFLOWY_MAILER_SMTP_EMAIL=${APPFLOWY_MAILER_SMTP_EMAIL}
- APPFLOWY_MAILER_SMTP_PASSWORD=${APPFLOWY_MAILER_SMTP_PASSWORD}
- APPFLOWY_MAILER_SMTP_TLS_KIND=${APPFLOWY_MAILER_SMTP_TLS_KIND:-wrapper}
appflowy_web:
restart: on-failure
image: appflowyinc/appflowy_web:${APPFLOWY_WEB_VERSION:-latest}
depends_on:
- appflowy_cloud
environment:
- AF_BASE_URL=${APPFLOWY_BASE_URL}
- AF_GOTRUE_URL=${APPFLOWY_BASE_URL}/gotrue
- AF_WS_V2_URL=${APPFLOWY_WEBSOCKET_BASE_URL}
volumes:
postgres_data:
minio_data:
-461
View File
@@ -1,461 +0,0 @@
[variables]
main_domain = "${domain}"
[config]
env = [
# =============================================================================
# 🌐 CORE DOMAIN CONFIGURATION
# =============================================================================
"FQDN=${main_domain}",
"SCHEME=https",
"WS_SCHEME=wss",
"APPFLOWY_BASE_URL=https://${main_domain}",
"APPFLOWY_WEBSOCKET_BASE_URL=wss://${main_domain}/ws/v2",
"APPFLOWY_WEB_URL=https://${main_domain}",
"API_EXTERNAL_URL=https://${main_domain}/gotrue",
"TZ=UTC",
# Admin Configuration
"GOTRUE_ADMIN_EMAIL=${email}",
"GOTRUE_ADMIN_PASSWORD=${password:16}",
"GOTRUE_DISABLE_SIGNUP=false",
# =============================================================================
# 🗄️ DATABASE & CACHE CONFIGURATION
# =============================================================================
"POSTGRES_HOST=postgres",
"POSTGRES_USER=appflowy",
"POSTGRES_PASSWORD=${password:64}",
"POSTGRES_PORT=5432",
"POSTGRES_DB=appflowy",
"REDIS_HOST=redis",
"REDIS_PORT=6379",
# =============================================================================
# 🔐 GOTRUE AUTHENTICATION CONFIGURATION
# =============================================================================
# JWT Configuration
"GOTRUE_JWT_SECRET=${password:64}",
"GOTRUE_JWT_EXP=7200",
"GOTRUE_JWT_ADMIN_GROUP_NAME=supabase_admin",
# Database Configuration
"GOTRUE_DB_DRIVER=postgres",
"GOTRUE_DATABASE_URL=postgres://appflowy:${POSTGRES_PASSWORD}@postgres:5432/appflowy?search_path=auth",
"PORT=9999",
# Site Configuration
"GOTRUE_SITE_URL=appflowy-flutter://",
"GOTRUE_URI_ALLOW_LIST=**",
# Email Configuration (SMTP - Configure for production)
"GOTRUE_SMTP_HOST=",
"GOTRUE_SMTP_PORT=465",
"GOTRUE_SMTP_USER=",
"GOTRUE_SMTP_PASS=",
"GOTRUE_SMTP_ADMIN_EMAIL=${GOTRUE_ADMIN_EMAIL}",
"GOTRUE_SMTP_MAX_FREQUENCY=1ns",
"GOTRUE_RATE_LIMIT_EMAIL_SENT=100",
"GOTRUE_MAILER_AUTOCONFIRM=true",
# Email Templates
"GOTRUE_MAILER_URLPATHS_CONFIRMATION=/gotrue/verify",
"GOTRUE_MAILER_URLPATHS_INVITE=/gotrue/verify",
"GOTRUE_MAILER_URLPATHS_RECOVERY=/gotrue/verify",
"GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE=/gotrue/verify",
"GOTRUE_MAILER_TEMPLATES_MAGIC_LINK=",
# OAuth Providers (Configure as needed)
"GOTRUE_EXTERNAL_GOOGLE_ENABLED=false",
"GOTRUE_EXTERNAL_GOOGLE_CLIENT_ID=",
"GOTRUE_EXTERNAL_GOOGLE_SECRET=",
"GOTRUE_EXTERNAL_GOOGLE_REDIRECT_URI=https://${main_domain}/gotrue/callback",
"GOTRUE_EXTERNAL_GITHUB_ENABLED=false",
"GOTRUE_EXTERNAL_GITHUB_CLIENT_ID=",
"GOTRUE_EXTERNAL_GITHUB_SECRET=",
"GOTRUE_EXTERNAL_GITHUB_REDIRECT_URI=https://${main_domain}/gotrue/callback",
"GOTRUE_EXTERNAL_DISCORD_ENABLED=false",
"GOTRUE_EXTERNAL_DISCORD_CLIENT_ID=",
"GOTRUE_EXTERNAL_DISCORD_SECRET=",
"GOTRUE_EXTERNAL_DISCORD_REDIRECT_URI=https://${main_domain}/gotrue/callback",
# SAML Configuration
"GOTRUE_SAML_ENABLED=false",
"GOTRUE_SAML_PRIVATE_KEY=",
# =============================================================================
# ☁️ APPFLOWY CLOUD SERVICE CONFIGURATION
# =============================================================================
# Core Configuration
"RUST_LOG=info",
"APPFLOWY_ENVIRONMENT=production",
"APPFLOWY_DATABASE_URL=postgres://appflowy:${POSTGRES_PASSWORD}@postgres:5432/appflowy",
"APPFLOWY_REDIS_URI=redis://redis:6379",
# Authentication Integration
"APPFLOWY_GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET}",
"APPFLOWY_GOTRUE_JWT_EXP=7200",
"APPFLOWY_GOTRUE_BASE_URL=http://gotrue:9999",
# Access Control and Performance
"APPFLOWY_ACCESS_CONTROL=true",
"APPFLOWY_DATABASE_MAX_CONNECTIONS=40",
"APPFLOWY_WEBSOCKET_MAILBOX_SIZE=6000",
# Email Configuration (SMTP)
"APPFLOWY_MAILER_SMTP_HOST=",
"APPFLOWY_MAILER_SMTP_PORT=465",
"APPFLOWY_MAILER_SMTP_USERNAME=",
"APPFLOWY_MAILER_SMTP_EMAIL=",
"APPFLOWY_MAILER_SMTP_PASSWORD=",
"APPFLOWY_MAILER_SMTP_TLS_KIND=wrapper",
# =============================================================================
# 💾 FILE STORAGE CONFIGURATION (MinIO/S3)
# =============================================================================
# MinIO Configuration
"MINIO_HOST=minio",
"MINIO_PORT=9000",
"APPFLOWY_S3_USE_MINIO=true",
"APPFLOWY_S3_CREATE_BUCKET=true",
"APPFLOWY_S3_MINIO_URL=http://minio:9000",
# Storage Credentials
"APPFLOWY_S3_ACCESS_KEY=${password:16}",
"APPFLOWY_S3_SECRET_KEY=${password:32}",
# Storage Configuration
"APPFLOWY_S3_BUCKET=appflowy",
"APPFLOWY_S3_REGION=us-east-1",
"APPFLOWY_S3_PRESIGNED_URL_ENDPOINT=https://${main_domain}/minio-api",
# AWS S3 Configuration (Alternative to MinIO)
# "APPFLOWY_S3_USE_MINIO=false",
# "APPFLOWY_S3_REGION=us-east-1",
# =============================================================================
# 🎛️ ADMIN FRONTEND CONFIGURATION
# =============================================================================
"ADMIN_FRONTEND_REDIS_URL=redis://redis:6379",
"ADMIN_FRONTEND_GOTRUE_URL=http://gotrue:9999",
"ADMIN_FRONTEND_APPFLOWY_CLOUD_URL=http://appflowy_cloud:8000",
"ADMIN_FRONTEND_PATH_PREFIX=/console",
# =============================================================================
# 🤖 AI FEATURES CONFIGURATION (Optional)
# =============================================================================
# OpenAI Configuration
"AI_OPENAI_API_KEY=",
"DEFAULT_AI_MODEL=gpt-4o-mini",
"DEFAULT_AI_COMPLETION_MODEL=gpt-4o-mini",
# Azure OpenAI (Alternative)
"AZURE_OPENAI_API_KEY=",
"AZURE_OPENAI_ENDPOINT=",
"AZURE_OPENAI_API_VERSION=",
# AI Service Configuration
"AI_SERVER_HOST=ai",
"AI_SERVER_PORT=5001",
"AI_DATABASE_URL=postgresql+psycopg://appflowy:${POSTGRES_PASSWORD}@postgres:5432/appflowy",
"AI_REDIS_URL=redis://redis:6379",
"AI_USE_MINIO=true",
"AI_MINIO_URL=http://minio:9000",
"AI_APPFLOWY_HOST=https://${main_domain}",
# Embedding Configuration
"APPFLOWY_EMBEDDING_CHUNK_SIZE=2000",
"APPFLOWY_EMBEDDING_CHUNK_OVERLAP=200",
# =============================================================================
# ⚙️ WORKER SERVICES CONFIGURATION
# =============================================================================
# AppFlowy Worker
"APPFLOWY_WORKER_REDIS_URL=redis://redis:6379",
"APPFLOWY_WORKER_ENVIRONMENT=production",
"APPFLOWY_WORKER_DATABASE_URL=postgres://appflowy:${POSTGRES_PASSWORD}@postgres:5432/appflowy",
"APPFLOWY_WORKER_DATABASE_NAME=appflowy",
"APPFLOWY_WORKER_IMPORT_TICK_INTERVAL=30",
# Indexer Configuration
"APPFLOWY_INDEXER_ENABLED=true",
"APPFLOWY_INDEXER_DATABASE_URL=postgres://appflowy:${POSTGRES_PASSWORD}@postgres:5432/appflowy",
"APPFLOWY_INDEXER_REDIS_URL=redis://redis:6379",
"APPFLOWY_INDEXER_EMBEDDING_BUFFER_SIZE=5000",
# Collaboration Service
"APPFLOWY_COLLABORATE_MULTI_THREAD=false",
"APPFLOWY_COLLABORATE_REMOVE_BATCH_SIZE=100",
# =============================================================================
# 🌐 NGINX CONFIGURATION
# =============================================================================
"NGINX_PORT=80",
"NGINX_TLS_PORT=443",
# =============================================================================
# 🛠️ VERSION TAGS (Easily Configurable)
# =============================================================================
"GOTRUE_VERSION=latest",
"APPFLOWY_CLOUD_VERSION=latest",
"APPFLOWY_ADMIN_FRONTEND_VERSION=latest",
"APPFLOWY_AI_VERSION=latest",
"APPFLOWY_WORKER_VERSION=latest",
"APPFLOWY_WEB_VERSION=latest",
]
[[config.domains]]
serviceName = "nginx"
port = 80
host = "${main_domain}"
[[config.mounts]]
filePath = "/nginx/nginx.conf"
content = """# Minimal nginx configuration for AppFlowy-Cloud
# Self Hosted AppFlowy Cloud user should alter this file to suit their needs,
# or use the appflowy.site.conf in external_proxy_config/nginx if they are using
# an external proxy.
events {
worker_connections 1024;
}
http {
# docker dns resolver
resolver 127.0.0.11 valid=10s;
#error_log /var/log/nginx/error.log debug;
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
map $http_origin $cors_origin {
# AppFlowy Web origin
"~^http://localhost:3000$" $http_origin;
default "null";
}
server {
listen 8080;
# https://github.com/nginxinc/nginx-prometheus-exporter
location = /stub_status {
stub_status;
}
}
server {
listen 80;
client_max_body_size 10M;
underscores_in_headers on;
set $appflowy_cloud_backend "http://appflowy_cloud:8000";
set $gotrue_backend "http://gotrue:9999";
set $admin_frontend_backend "http://admin_frontend:3000";
set $appflowy_web_backend "http://appflowy_web:80";
set $minio_backend "http://minio:9001";
set $minio_api_backend "http://minio:9000";
# Host name for minio, used internally within docker compose
set $minio_internal_host "minio:9000";
set $pgadmin_backend "http://pgadmin:80";
# GoTrue
location /gotrue/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Headers' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Max-Age' 3600 always;
add_header 'Content-Type' 'text/plain charset=UTF-8' always;
add_header 'Content-Length' 0 always;
return 204;
}
proxy_pass $gotrue_backend;
rewrite ^/gotrue(/.*)$ $1 break;
# Allow headers like redirect_to to be handed over to the gotrue
# for correct redirecting
proxy_set_header Host $http_host;
proxy_pass_request_headers on;
}
# WebSocket
location /ws {
proxy_pass $appflowy_cloud_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400s;
}
location /api {
proxy_pass $appflowy_cloud_backend;
proxy_set_header X-Request-Id $request_id;
proxy_set_header Host $http_host;
# Set CORS headers for other requests
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always;
add_header 'Access-Control-Max-Age' 3600 always;
return 204;
}
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always;
add_header 'Access-Control-Max-Age' 3600 always;
location ~* ^/api/workspace/([a-zA-Z0-9_-]+)/publish$ {
proxy_pass $appflowy_cloud_backend;
proxy_request_buffering off;
client_max_body_size 256M;
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always;
add_header 'Access-Control-Max-Age' 3600 always;
return 204;
}
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Client-Version, Device-Id' always;
add_header 'Access-Control-Max-Age' 3600 always;
}
# AppFlowy-Cloud
location /api/chat {
proxy_pass $appflowy_cloud_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding on;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 600s;
proxy_connect_timeout 600s;
proxy_send_timeout 600s;
}
location /api/import {
proxy_pass $appflowy_cloud_backend;
# Set headers
proxy_set_header X-Request-Id $request_id;
proxy_set_header Host $http_host;
# Handle CORS
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, PATCH, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, Accept, Device-Id' always;
add_header 'Access-Control-Max-Age' 3600 always;
# Timeouts
proxy_read_timeout 600s;
proxy_connect_timeout 600s;
proxy_send_timeout 600s;
# Disable buffering for large file uploads
proxy_request_buffering off;
proxy_buffering off;
proxy_cache off;
client_max_body_size 2G;
}
}
# Minio Web UI
# Derive from: https://min.io/docs/minio/linux/integrations/setup-nginx-proxy-with-minio.html
# Optional Module, comment this section if you did not deploy minio in docker-compose.yml
# This endpoint is meant to be used for the MinIO Web UI, accessible via the admin portal
location /minio/ {
proxy_pass $minio_backend;
rewrite ^/minio/(.*) /$1 break;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
## This is necessary to pass the correct IP to be hashed
real_ip_header X-Real-IP;
proxy_connect_timeout 300s;
## To support websockets in MinIO versions released after January 2023
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Some environments may encounter CORS errors (Kubernetes + Nginx Ingress)
# Uncomment the following line to set the Origin request to an empty string
# proxy_set_header Origin '';
chunked_transfer_encoding off;
}
# Optional Module, comment this section if you did not deploy minio in docker-compose.yml
# This is used for presigned url, which is needs to be exposed to the AppFlowy client application.
location /minio-api/ {
proxy_pass $minio_api_backend;
# Set the host to internal host because the presigned url was signed against the internal host
proxy_set_header Host $minio_internal_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
rewrite ^/minio-api/(.*) /$1 break;
proxy_connect_timeout 300s;
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
}
# PgAdmin
# Optional Module, comment this section if you did not deploy pgadmin in docker-compose.yml
location /pgadmin/ {
set $pgadmin pgadmin;
proxy_pass $pgadmin_backend;
proxy_set_header X-Script-Name /pgadmin;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $host;
proxy_redirect off;
}
# Admin Frontend
# Optional Module, comment this section if you did not deploy admin_frontend in docker-compose.yml
location /console {
proxy_pass $admin_frontend_backend;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $host;
}
# AppFlowy Web
location / {
proxy_pass $appflowy_web_backend;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $host;
}
}
}
"""
-16
View File
@@ -1,16 +0,0 @@
version: "3.8"
services:
apprise-api:
image: linuxserver/apprise-api:latest
restart: unless-stopped
ports:
- 8000
environment:
- PUID=1000
- PGID=1000
- TZ=UTC
volumes:
- config:/config
volumes:
config: {}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

-8
View File
@@ -1,8 +0,0 @@
[variables]
main_domain = "${domain}"
[config]
[[config.domains]]
serviceName = "apprise-api"
port = 8000
host = "${main_domain}"
-11
View File
@@ -1,11 +0,0 @@
[variables]
main_domain = "${domain}"
[config]
env = []
mounts = []
[[config.domains]]
serviceName = "appsmith"
port = 80
host = "${main_domain}"
+12
View File
@@ -0,0 +1,12 @@
variables:
main_domain: ${domain}
config:
domains:
- serviceName: appsmith
port: 80
host: ${main_domain}
env: []
mounts: []
+18 -18
View File
@@ -9,7 +9,7 @@ x-logging: &x-logging
services:
appwrite:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
container_name: appwrite
<<: *x-logging
restart: unless-stopped
@@ -195,7 +195,7 @@ services:
- _APP_STORAGE_WASABI_BUCKET
appwrite-realtime:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: realtime
container_name: appwrite-realtime
<<: *x-logging
@@ -227,7 +227,7 @@ services:
- _APP_LOGGING_CONFIG
appwrite-worker-audits:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-audits
<<: *x-logging
container_name: appwrite-worker-audits
@@ -253,7 +253,7 @@ services:
- _APP_LOGGING_CONFIG
appwrite-worker-webhooks:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-webhooks
<<: *x-logging
container_name: appwrite-worker-webhooks
@@ -281,7 +281,7 @@ services:
- _APP_LOGGING_CONFIG
appwrite-worker-deletes:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-deletes
<<: *x-logging
container_name: appwrite-worker-deletes
@@ -339,7 +339,7 @@ services:
- _APP_MAINTENANCE_RETENTION_EXECUTION
appwrite-worker-databases:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-databases
<<: *x-logging
container_name: appwrite-worker-databases
@@ -365,7 +365,7 @@ services:
- _APP_LOGGING_CONFIG
appwrite-worker-builds:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-builds
<<: *x-logging
container_name: appwrite-worker-builds
@@ -428,7 +428,7 @@ services:
- _APP_STORAGE_WASABI_BUCKET
appwrite-worker-certificates:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-certificates
<<: *x-logging
container_name: appwrite-worker-certificates
@@ -461,7 +461,7 @@ services:
- _APP_LOGGING_CONFIG
appwrite-worker-functions:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-functions
<<: *x-logging
container_name: appwrite-worker-functions
@@ -499,7 +499,7 @@ services:
- _APP_LOGGING_CONFIG
appwrite-worker-mails:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-mails
<<: *x-logging
container_name: appwrite-worker-mails
@@ -531,7 +531,7 @@ services:
- _APP_LOGGING_CONFIG
appwrite-worker-messaging:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-messaging
container_name: appwrite-worker-messaging
<<: *x-logging
@@ -581,7 +581,7 @@ services:
- _APP_STORAGE_WASABI_BUCKET
appwrite-worker-migrations:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-migrations
<<: *x-logging
container_name: appwrite-worker-migrations
@@ -611,7 +611,7 @@ services:
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
appwrite-task-maintenance:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: maintenance
<<: *x-logging
container_name: appwrite-task-maintenance
@@ -645,7 +645,7 @@ services:
- _APP_MAINTENANCE_RETENTION_SCHEDULES
appwrite-worker-usage:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-usage
container_name: appwrite-worker-usage
<<: *x-logging
@@ -673,7 +673,7 @@ services:
- _APP_USAGE_AGGREGATION_INTERVAL
appwrite-worker-usage-dump:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: worker-usage-dump
container_name: appwrite-worker-usage-dump
<<: *x-logging
@@ -700,7 +700,7 @@ services:
- _APP_USAGE_AGGREGATION_INTERVAL
appwrite-task-scheduler-functions:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: schedule-functions
container_name: appwrite-task-scheduler-functions
<<: *x-logging
@@ -725,7 +725,7 @@ services:
- _APP_DB_PASS
appwrite-task-scheduler-executions:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: schedule-executions
container_name: appwrite-task-scheduler-executions
<<: *x-logging
@@ -750,7 +750,7 @@ services:
- _APP_DB_PASS
appwrite-task-scheduler-messages:
image: appwrite/appwrite:1.6.1
image: appwrite/appwrite:1.6.0
entrypoint: schedule-messages
container_name: appwrite-task-scheduler-messages
<<: *x-logging
-143
View File
@@ -1,143 +0,0 @@
[variables]
main_domain = "${domain}"
[config]
env = [
"_APP_ENV=production",
"_APP_LOCALE=en",
"_APP_OPTIONS_ABUSE=enabled",
"_APP_OPTIONS_FORCE_HTTPS=disabled",
"_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled",
"_APP_OPTIONS_ROUTER_PROTECTION=disabled",
"_APP_OPENSSL_KEY_V1=your-secret-key",
"_APP_DOMAIN=${main_domain}",
"_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_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_USAGE_STATS=enabled",
"_APP_LOGGING_PROVIDER=",
"_APP_LOGGING_CONFIG=",
"_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_HOST=mariadb",
"_APP_DB_PORT=3306",
"_APP_DB_SCHEMA=appwrite",
"_APP_DB_USER=user",
"_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=",
"_APP_SMTP_USERNAME=",
"_APP_SMTP_PASSWORD=",
"_APP_SMS_PROVIDER=",
"_APP_SMS_FROM=",
"_APP_STORAGE_LIMIT=30000000",
"_APP_STORAGE_PREVIEW_LIMIT=20000000",
"_APP_STORAGE_ANTIVIRUS=disabled",
"_APP_STORAGE_ANTIVIRUS_HOST=clamav",
"_APP_STORAGE_ANTIVIRUS_PORT=3310",
"_APP_STORAGE_DEVICE=local",
"_APP_STORAGE_S3_ACCESS_KEY=",
"_APP_STORAGE_S3_SECRET=",
"_APP_STORAGE_S3_REGION=us-east-1",
"_APP_STORAGE_S3_BUCKET=",
"_APP_STORAGE_DO_SPACES_ACCESS_KEY=",
"_APP_STORAGE_DO_SPACES_SECRET=",
"_APP_STORAGE_DO_SPACES_REGION=us-east-1",
"_APP_STORAGE_DO_SPACES_BUCKET=",
"_APP_STORAGE_BACKBLAZE_ACCESS_KEY=",
"_APP_STORAGE_BACKBLAZE_SECRET=",
"_APP_STORAGE_BACKBLAZE_REGION=us-west-004",
"_APP_STORAGE_BACKBLAZE_BUCKET=",
"_APP_STORAGE_LINODE_ACCESS_KEY=",
"_APP_STORAGE_LINODE_SECRET=",
"_APP_STORAGE_LINODE_REGION=eu-central-1",
"_APP_STORAGE_LINODE_BUCKET=",
"_APP_STORAGE_WASABI_ACCESS_KEY=",
"_APP_STORAGE_WASABI_SECRET=",
"_APP_STORAGE_WASABI_REGION=eu-central-1",
"_APP_STORAGE_WASABI_BUCKET=",
"_APP_FUNCTIONS_SIZE_LIMIT=30000000",
"_APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000",
"_APP_FUNCTIONS_TIMEOUT=900",
"_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_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_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_ASSISTANT_OPENAI_API_KEY=",
]
mounts = []
[[config.domains]]
serviceName = "appwrite"
port = 80
host = "${main_domain}"
path = "/"
[[config.domains]]
serviceName = "appwrite-console"
port = 80
host = "${main_domain}"
path = "/console"
[[config.domains]]
serviceName = "appwrite-realtime"
port = 80
host = "${main_domain}"
path = "/v1/realtime"
+139
View File
@@ -0,0 +1,139 @@
variables:
main_domain: ${domain}
config:
domains:
- serviceName: appwrite
port: 80
host: ${main_domain}
path: /
- serviceName: appwrite-console
port: 80
host: ${main_domain}
path: /console
- serviceName: appwrite-realtime
port: 80
host: ${main_domain}
path: /v1/realtime
env:
- _APP_ENV=production
- _APP_LOCALE=en
- _APP_OPTIONS_ABUSE=enabled
- _APP_OPTIONS_FORCE_HTTPS=disabled
- _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled
- _APP_OPTIONS_ROUTER_PROTECTION=disabled
- _APP_OPENSSL_KEY_V1=your-secret-key
- _APP_DOMAIN=${main_domain}
- _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_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_USAGE_STATS=enabled
- _APP_LOGGING_PROVIDER=
- _APP_LOGGING_CONFIG=
- _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_HOST=mariadb
- _APP_DB_PORT=3306
- _APP_DB_SCHEMA=appwrite
- _APP_DB_USER=user
- _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=
- _APP_SMTP_USERNAME=
- _APP_SMTP_PASSWORD=
- _APP_SMS_PROVIDER=
- _APP_SMS_FROM=
- _APP_STORAGE_LIMIT=30000000
- _APP_STORAGE_PREVIEW_LIMIT=20000000
- _APP_STORAGE_ANTIVIRUS=disabled
- _APP_STORAGE_ANTIVIRUS_HOST=clamav
- _APP_STORAGE_ANTIVIRUS_PORT=3310
- _APP_STORAGE_DEVICE=local
- _APP_STORAGE_S3_ACCESS_KEY=
- _APP_STORAGE_S3_SECRET=
- _APP_STORAGE_S3_REGION=us-east-1
- _APP_STORAGE_S3_BUCKET=
- _APP_STORAGE_DO_SPACES_ACCESS_KEY=
- _APP_STORAGE_DO_SPACES_SECRET=
- _APP_STORAGE_DO_SPACES_REGION=us-east-1
- _APP_STORAGE_DO_SPACES_BUCKET=
- _APP_STORAGE_BACKBLAZE_ACCESS_KEY=
- _APP_STORAGE_BACKBLAZE_SECRET=
- _APP_STORAGE_BACKBLAZE_REGION=us-west-004
- _APP_STORAGE_BACKBLAZE_BUCKET=
- _APP_STORAGE_LINODE_ACCESS_KEY=
- _APP_STORAGE_LINODE_SECRET=
- _APP_STORAGE_LINODE_REGION=eu-central-1
- _APP_STORAGE_LINODE_BUCKET=
- _APP_STORAGE_WASABI_ACCESS_KEY=
- _APP_STORAGE_WASABI_SECRET=
- _APP_STORAGE_WASABI_REGION=eu-central-1
- _APP_STORAGE_WASABI_BUCKET=
- _APP_FUNCTIONS_SIZE_LIMIT=30000000
- _APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000
- _APP_FUNCTIONS_TIMEOUT=900
- _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_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_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_ASSISTANT_OPENAI_API_KEY=
mounts: []
-12
View File
@@ -1,12 +0,0 @@
[variables]
main_domain = "${domain}"
auth_secret = "${base64:32}"
[config]
env = ["APTABASE_HOST=${main_domain}", "AUTH_SECRET=${auth_secret}"]
mounts = []
[[config.domains]]
serviceName = "aptabase"
port = 8_080
host = "${main_domain}"
+15
View File
@@ -0,0 +1,15 @@
variables:
main_domain: ${domain}
auth_secret: ${base64:32}
config:
domains:
- serviceName: aptabase
port: 8080
host: ${main_domain}
env:
- APTABASE_HOST=${main_domain}
- AUTH_SECRET=${auth_secret}
mounts: []
-14
View File
@@ -1,14 +0,0 @@
version: "3.8"
services:
arangodb:
image: arangodb:3.12.4
restart: unless-stopped
ports:
- 8529
environment:
- ARANGO_ROOT_PASSWORD=${ARANGO_PASSWORD}
volumes:
- data:/var/lib/arangodb3
volumes:
data: {}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

-12
View File
@@ -1,12 +0,0 @@
[variables]
main_domain = "${domain}"
arango_password = "${password:16}"
[config]
[[config.domains]]
serviceName = "arangodb"
port = 8529
host = "${main_domain}"
[config.env]
ARANGO_PASSWORD = "${arango_password}"
-77
View File
@@ -1,77 +0,0 @@
version: "3.8"
services:
argilla-web:
image: argilla/argilla-server:latest
restart: unless-stopped
ports:
- 6900
environment:
- ARGILLA_HOME_PATH=/var/lib/argilla
- ARGILLA_ELASTICSEARCH=http://argilla-elasticsearch:9200
- ARGILLA_DATABASE_URL=postgresql+asyncpg://postgres:${DB_PASSWORD}@argilla-db:5432/argilla
- ARGILLA_REDIS_URL=redis://:${REDIS_PASSWORD}@argilla-redis:6379/0
- USERNAME=${LOGIN_USERNAME}
- PASSWORD=${LOGIN_PASSWORD}
- API_KEY=argilla.apikey
- WORKSPACE=default
volumes:
- argilladata:/var/lib/argilla
depends_on:
- argilla-elasticsearch
- argilla-db
- argilla-redis
argilla-worker:
image: argilla/argilla-server:latest
restart: unless-stopped
environment:
- BACKGROUND_NUM_WORKERS=2
- ARGILLA_HOME_PATH=/var/lib/argilla
- ARGILLA_ELASTICSEARCH=http://argilla-elasticsearch:9200
- ARGILLA_DATABASE_URL=postgresql+asyncpg://postgres:${DB_PASSWORD}@argilla-db:5432/argilla
- ARGILLA_REDIS_URL=redis://:${REDIS_PASSWORD}@argilla-redis:6379/0
volumes:
- argilladata:/var/lib/argilla
command: python -m argilla_server worker --num-workers ${BACKGROUND_NUM_WORKERS}
depends_on:
- argilla-elasticsearch
- argilla-db
- argilla-redis
argilla-elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.12.2
restart: unless-stopped
environment:
- node.name=elasticsearch
- cluster.name=es-argilla-local
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m
- cluster.routing.allocation.disk.threshold_enabled=false
- xpack.security.enabled=false
volumes:
- elasticdata:/usr/share/elasticsearch/data
argilla-db:
image: postgres:15-alpine
restart: unless-stopped
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=argilla
volumes:
- dbdata:/var/lib/postgresql/data
argilla-redis:
image: redis:7-alpine
restart: unless-stopped
environment:
- REDIS_PASSWORD=${REDIS_PASSWORD}
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redisdata:/data
volumes:
argilladata: {}
elasticdata: {}
dbdata: {}
redisdata: {}
-12
View File
@@ -1,12 +0,0 @@
<svg width="481" height="252" viewBox="0 0 481 252" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M125.336 251.397C194.557 251.397 250.671 195.216 250.671 125.913C250.671 56.6109 194.557 0.430069 125.336 0.430069C56.1147 0.430069 0 56.6109 0 125.913C0 195.216 56.1147 251.397 125.336 251.397Z" fill="url(#paint0_linear_0_1)"/>
<path d="M479.93 251.397C342.032 251.397 229.83 139.062 229.83 1.00175H317.193C317.193 91.0411 390.282 164.216 480.215 164.216V251.397H479.93Z" fill="#F8C0A7"/>
<defs>
<linearGradient id="paint0_linear_0_1" x1="55.414" y1="60.2073" x2="211.749" y2="206.798" gradientUnits="userSpaceOnUse">
<stop offset="0.0151877" stop-color="#FF675F"/>
<stop offset="1" stop-color="#F73D3D"/>
</linearGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 751 B

-19
View File
@@ -1,19 +0,0 @@
[variables]
main_domain = "${domain}"
login_username = "${username}"
login_password = "${password:8}"
db_password = "${password:16}"
redis_password = "${password:16}"
[config]
[[config.domains]]
serviceName = "argilla-web"
port = 6900
host = "${main_domain}"
[config.env]
LOGIN_USERNAME = "${login_username}"
LOGIN_PASSWORD = "${login_password}"
DB_PASSWORD = "${db_password}"
REDIS_PASSWORD = "${redis_password}"
BACKGROUND_NUM_WORKERS = "2"
@@ -1,17 +0,0 @@
version: "3.8"
services:
audiobookshelf:
image: ghcr.io/advplyr/audiobookshelf:2.19.4
restart: unless-stopped
ports:
- 80
environment:
- TZ=UTC
volumes:
- config:/config
- metadata:/metadata
- ${AUDIOBOOKS_PATH}:/audiobooks
volumes:
config: {}
metadata: {}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

-12
View File
@@ -1,12 +0,0 @@
[variables]
main_domain = "${domain}"
audiobooks_path = "/path/to/your/audiobooks"
[config]
[[config.domains]]
serviceName = "audiobookshelf"
port = 80
host = "${main_domain}"
[config.env]
AUDIOBOOKS_PATH = "${audiobooks_path}"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

-55
View File
@@ -1,55 +0,0 @@
services:
authelia:
image: authelia/authelia:latest
restart: unless-stopped
volumes:
- authelia_config:/config
- ../files/configuration.yml:/config/configuration.yml:ro
- ../files/users_database.yml:/config/users_database.yml
environment:
AUTHELIA_JWT_SECRET: $JWT_SECRET
AUTHELIA_SESSION_SECRET: $SESSION_SECRET
AUTHELIA_STORAGE_ENCRYPTION_KEY: $STORAGE_ENCRYPTION_KEY
AUTHELIA_STORAGE_POSTGRES_PASSWORD: $POSTGRES_PASSWORD
depends_on:
redis:
condition: service_healthy
postgres:
condition: service_healthy
ports:
- 9091
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis_data:/data
command: redis-server --save 60 1 --loglevel warning --requirepass $REDIS_PASSWORD
environment:
REDIS_PASSWORD: $REDIS_PASSWORD
healthcheck:
test: ["CMD", "redis-cli", "--raw", "incr", "ping"]
interval: 10s
timeout: 3s
retries: 5
postgres:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: authelia
POSTGRES_USER: authelia
POSTGRES_PASSWORD: $POSTGRES_PASSWORD
healthcheck:
test: ["CMD-SHELL", "pg_isready -U authelia -d authelia"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
volumes:
authelia_config:
redis_data:
postgres_data:
-195
View File
@@ -1,195 +0,0 @@
[variables]
main_domain = "${domain}"
jwt_secret = "${password:64}"
session_secret = "${password:64}"
storage_encryption_key = "${password:64}"
redis_password = "${password:32}"
postgres_password = "${password:32}"
admin_username = "${username}"
admin_email = "${email}"
admin_password = "AdminPass123!"
admin_password_hash = "$argon2id$v=19$m=65536,t=3,p=4$170PGJ1MskQyxfFknfBPFQ$VqD1/pqC3fBHo+Zk58bC2xQm1ltOFTr0w2wx93vJgC4"
[config]
[[config.domains]]
serviceName = "authelia"
port = 9091
host = "${main_domain}"
path = "/"
[config.env]
JWT_SECRET = "${jwt_secret}"
SESSION_SECRET = "${session_secret}"
STORAGE_ENCRYPTION_KEY = "${storage_encryption_key}"
REDIS_PASSWORD = "${redis_password}"
POSTGRES_PASSWORD = "${postgres_password}"
admin_username = "${admin_username}"
admin_email = "${admin_email}"
[[config.mounts]]
filePath = "configuration.yml"
content = """
###############################################################
# Authelia configuration #
###############################################################
# DEFAULT ADMIN CREDENTIALS:
# Username: (auto-generated, check users_database.yml)
# Password: AdminPass123!
# Email: (auto-generated)
#
# IMPORTANT: Change the password after first login!
# SECURITY NOTE: This template starts with one-factor auth for easier setup.
# After configuring SMTP/notifications, change the policy to 'two_factor'
# Server Configuration
server:
address: 'tcp://0.0.0.0:9091'
headers:
csp_template: ''
# Log Configuration
log:
level: info
format: text
# Theme
theme: auto
# TOTP Configuration
totp:
disable: false
issuer: authelia.com
algorithm: sha1
digits: 6
period: 30
skew: 1
secret_size: 32
# WebAuthn/FIDO2 Configuration
webauthn:
disable: false
timeout: 60s
display_name: Authelia
attestation_conveyance_preference: indirect
user_verification: preferred
# NTP Configuration
ntp:
address: 'time.cloudflare.com:123'
version: 4
max_desync: 3s
disable_startup_check: false
disable_failure: false
# Authentication Backend Configuration
authentication_backend:
password_reset:
disable: false
custom_url: ''
refresh_interval: 5m
file:
path: /config/users_database.yml
watch: false
search:
email: false
case_insensitive: false
password:
algorithm: argon2
argon2:
variant: argon2id
iterations: 3
memory: 65536
parallelism: 4
key_length: 32
salt_length: 16
# Password Policy
password_policy:
standard:
enabled: false
min_length: 8
max_length: 0
require_uppercase: true
require_lowercase: true
require_number: true
require_special: true
zxcvbn:
enabled: false
min_score: 3
# Session Configuration
session:
name: authelia_session
domain: ${main_domain}
same_site: lax
secret: ${session_secret}
expiration: 1h
inactivity: 5m
remember_me_duration: 1M
redis:
host: redis
port: 6379
password: ${redis_password}
database_index: 0
maximum_active_connections: 8
minimum_idle_connections: 0
# Storage Configuration
storage:
encryption_key: ${storage_encryption_key}
postgres:
host: postgres
port: 5432
database: authelia
schema: public
username: authelia
password: ${postgres_password}
timeout: 5s
# Notifier Configuration
notifier:
disable_startup_check: true
filesystem:
filename: /config/notification.txt
# Regulation Configuration
regulation:
max_retries: 3
find_time: 10m
ban_time: 12h
# Access Control Configuration - MODIFIED FOR EASIER INITIAL SETUP
access_control:
default_policy: deny
rules:
- domain: ${main_domain}
policy: one_factor # Changed from one_factor to two_factor for production
"""
[[config.mounts]]
filePath = "users_database.yml"
content = """
###############################################################
# Users Database #
###############################################################
# DEFAULT LOGIN CREDENTIALS:
# Username: (generated from username helper)
# Password: AdminPass123!
# Email: (generated from email helper)
#
# IMPORTANT: Change the default password after first login!
# To generate a new password hash, run:
# docker run authelia/authelia:latest authelia hash-password 'your-new-password'
users:
${admin_username}:
disabled: false
displayname: "Authelia Admin"
password: "${admin_password_hash}"
email: ${admin_email}
groups:
- admins
- dev
"""

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