Compare commits

..
1 Commits
1999 changed files with 7180 additions and 65801 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
@@ -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
-73
View File
@@ -1,73 +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**:
- **REQUIREMENT**: Service **MUST** be open source. Only add templates for applications with an open-source license (e.g., MIT, Apache, GPL, AGPL). Proprietary or closed-source services are not allowed.
- **Verify Docker Images**: Before using any Docker image in `docker-compose.yml`, verify it exists using `docker manifest inspect <image:tag>` (e.g., `docker manifest inspect docker.io/bitnami/discourse:3.5.0`). This ensures the image is available and prevents deployment failures.
- Create `blueprints/<id>/` (e.g., `ghost`).
- Implement `docker-compose.yml` (single service typical; use volumes for persistence).
- Configure `template.toml`—reference vars in `[config.domains]`, `[config.env]`, `[config.mounts]`.
- 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
- **Open Source Requirement**: **ALL services MUST be open source**. Only applications with open-source licenses (MIT, Apache, GPL, AGPL, etc.) are allowed. Proprietary or closed-source services are strictly prohibited.
- **Template IDs**: Lowercase, kebab-case (e.g., `active-pieces`); unique across repo—enforced by dedupe script.
- **Docker Compose**: Minimal—omit `ports` (Dokploy proxies), persistent volumes (e.g., `- db-data:/var/lib/postgresql/data`). Services named after folder (e.g., `ghost` service).
- **template.toml**:
- Variables: `[variables] main_domain = "${domain}"`; use helpers for secrets (`${password:64}`, `${base64:32}`).
- Domains: `[[config.domains]] serviceName = "<service>" port = 80 host = "${main_domain}"` (path="/" optional).
- Env: `[[config.env]]` array of "KEY=VALUE" strings, interpolating vars (e.g., "DB_PASSWORD=${db_pass}").
- **URL Variables**: When environment variables require URLs (e.g., `WEB_URL`, `NEXTAUTH_URL`, `PUBLIC_URL`), **always use HTTP by default** (e.g., `"http://${main_domain}"`). HTTPS should only be used if explicitly required by the application or when using a reverse proxy with SSL termination.
- Mounts: `[[config.mounts]] filePath = "/etc/config" content = """multi-line\ncontent"""`.
- JWT helper: `${jwt:secret_var:payload_var}` for auth tokens; payload as JSON string with `exp: ${timestamps:YYYY-MM-DDTHH:mm:ssZ}`.
- **Meta.json**: Entries as JSON objects; tags array of lowercase strings (e.g., ["monitoring", "database"]); links object with `github`, `website`, `docs`.
- **No Networks**: Rely on Dokploy's isolated deployments—avoid explicit `networks:`.
- **Versions**: Pin images to specific versions in `docker-compose.yml` (e.g., `ghost:5.82.0-alpine`); match in `meta.json.version`. **NEVER use `latest` tag**—it can break templates when upstream images change unexpectedly. **Always verify image exists** using `docker manifest inspect <image:tag>` before committing.
- **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
@@ -20,7 +20,6 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup pnpm
uses: pnpm/action-setup@v2
with:
+1
View File
@@ -5,6 +5,7 @@ on:
workflows: [Build Preview Deployment]
types:
- completed
permissions:
actions: read
deployments: write
@@ -1,121 +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
- name: Set up Docker Compose
run: 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: cd build-scripts && pnpm install
- name: Detect changed blueprints
id: changed
run: |
BASE_SHA=$(git merge-base HEAD origin/${{ github.base_ref }})
# Obtener todos los blueprints que tienen cambios (en docker-compose.yml o template.toml)
CHANGED_BLUEPRINTS=$(git diff --name-only --diff-filter=ACMRT $BASE_SHA HEAD | \
grep -E 'blueprints/[^/]+/(docker-compose\.yml|template\.toml)$' | \
sed 's|blueprints/\([^/]*\)/.*|\1|' | \
sort -u)
echo "Changed blueprints:"
echo "$CHANGED_BLUEPRINTS" | while read blueprint; do
[ -n "$blueprint" ] && echo " - $blueprint"
done
# Guardar lista de blueprints (una por línea)
echo "blueprints<<EOF" >> $GITHUB_OUTPUT
echo "$CHANGED_BLUEPRINTS" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Validate blueprints
run: |
BLUEPRINTS="${{ steps.changed.outputs.blueprints }}"
if [ -z "$BLUEPRINTS" ] || [ "$BLUEPRINTS" = "" ]; then
echo "ℹ️ No blueprints changed, skipping validation"
exit 0
fi
ERROR=0
# Convertir a array para evitar problemas con subshells
mapfile -t BLUEPRINT_ARRAY <<< "$BLUEPRINTS"
# Iterar sobre cada blueprint
for blueprint in "${BLUEPRINT_ARRAY[@]}"; do
[ -z "$blueprint" ] && continue
BLUEPRINT_PATH="blueprints/$blueprint"
COMPOSE_FILE="$BLUEPRINT_PATH/docker-compose.yml"
TOML_FILE="$BLUEPRINT_PATH/template.toml"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "📦 Validating: $blueprint"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# 1. Validar best practices de docker-compose.yml
if [ ! -f "$COMPOSE_FILE" ]; then
echo "⚠️ WARNING: docker-compose.yml not found"
ERROR=1
continue
fi
echo "🔍 Validating docker-compose.yml best practices..."
if ! (cd build-scripts && pnpm exec tsx validate-docker-compose.ts --file "../$COMPOSE_FILE" --verbose); then
ERROR=1
continue
fi
# 3. Validar template.toml
if [ -f "$TOML_FILE" ]; then
echo "🔍 Validating template.toml..."
if ! (cd build-scripts && pnpm exec tsx validate-template.ts --dir "../$BLUEPRINT_PATH" --verbose); then
ERROR=1
continue
fi
else
echo "⚠️ WARNING: template.toml not found"
ERROR=1
continue
fi
echo "✅ All validations passed for $blueprint"
done
if [ $ERROR -eq 1 ]; then
echo ""
echo "❌ Validation failed for one or more blueprints"
exit 1
else
echo ""
echo "✅ All blueprints validated successfully"
fi
-41
View File
@@ -1,41 +0,0 @@
name: Validate Template Metadata
on:
push:
branches: [canary]
paths:
- "blueprints/**"
- "build-scripts/generate-meta.js"
- "meta.json"
pull_request:
branches: [canary]
paths:
- "blueprints/**"
- "build-scripts/generate-meta.js"
- "meta.json"
workflow_dispatch:
jobs:
validate-meta:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Reject root meta.json
run: |
if [ -f "meta.json" ]; then
echo "❌ The root meta.json is generated at build time and must not be committed."
echo " Add your template's metadata to blueprints/<id>/meta.json instead."
exit 1
fi
echo "✅ No root meta.json present"
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Validate per-template metadata
run: node build-scripts/generate-meta.js --check
+57 -10
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,10 +49,57 @@ jobs:
echo "✅ Blueprint folders validated successfully."
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Validate meta.json matches blueprint folders and logo files
run: |
echo "🔍 Validating meta.json against blueprint folders and logos..."
- name: Validate per-template metadata (blueprints/<id>/meta.json)
run: node build-scripts/generate-meta.js --check
ERROR=0
# Read all blueprint folder names into an array
FOLDERS=($(ls -1 blueprints))
# Extract ids and logos from meta.json
IDS_AND_LOGOS=$(jq -c '.[] | {id, logo}' meta.json)
# Validate each id in meta.json exists as a folder
for item in $IDS_AND_LOGOS; do
ID=$(echo "$item" | jq -r '.id')
LOGO=$(echo "$item" | jq -r '.logo')
# Check if folder exists
if [ ! -d "blueprints/$ID" ]; then
echo "❌ meta.json id \"$ID\" does not have a matching folder in blueprints/"
ERROR=1
continue
fi
# Check if logo file exists inside its folder
if [ ! -f "blueprints/$ID/$LOGO" ]; then
echo "❌ Logo \"$LOGO\" defined for \"$ID\" does not exist in blueprints/$ID/"
ERROR=1
fi
done
# Validate each folder has a matching id in meta.json
META_IDS=$(jq -r '.[].id' meta.json)
for FOLDER in "${FOLDERS[@]}"; do
FOUND=0
for ID in $META_IDS; do
if [ "$FOLDER" == "$ID" ]; then
FOUND=1
break
fi
done
if [ "$FOUND" -eq 0 ]; then
echo "❌ Folder \"$FOLDER\" has no matching id in meta.json"
ERROR=1
fi
done
if [ $ERROR -eq 1 ]; then
echo "❌ meta.json validation failed."
exit 1
else
echo "✅ meta.json validated successfully."
fi
-3
View File
@@ -1,3 +0,0 @@
node_modules
package-lock.json
meta.json.backup.*
-199
View File
@@ -1,199 +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`: Metadata tooling — `generate-meta.js` (aggregates and validates every `blueprints/<id>/meta.json` into the served `meta.json` artifact) and `explode-meta.js` (one-shot migration reference).
- `/.github/workflows`: CI/CD workflows for validation, preview builds, and deployments.
- Root level: There is NO root `meta.json` in the repo — each template's metadata lives in `blueprints/<id>/meta.json` and the global registry is generated at build time.
- **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: kebab-case for scripts (`generate-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:** `build-scripts/generate-meta.js` - Aggregates and validates all `blueprints/<id>/meta.json` files into the served `meta.json` artifact (`app/public/meta.json`, gitignored).
- **Frontend:** `app/src/main.tsx` - React application entry point.
- **Configuration:**
- `app/package.json` - Frontend dependencies and pnpm scripts (`dev` and `build` run `generate-meta.js` first).
- `blueprints/<id>/meta.json` - Per-template metadata (one object per template; 400+ templates).
- `app/vite.config.ts` - Vite build configuration with static copy plugin.
- `app/tsconfig.json` - TypeScript compiler configuration.
- **CI/CD Pipeline:**
- `.github/workflows/validate-meta.yml` - Validates every `blueprints/<id>/meta.json` (via `generate-meta.js --check`) and rejects a committed root `meta.json`.
- `.github/workflows/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
```
- **Validate template metadata** (CRITICAL: run after adding/editing any `blueprints/<id>/meta.json`)
```bash
node build-scripts/generate-meta.js --check
```
- **Generate the served meta.json artifact** (done automatically by `pnpm dev` / `pnpm build` in `app/`)
```bash
node build-scripts/generate-meta.js # writes app/public/meta.json
```
- **Task Configuration:**
- **App scripts (`app/package.json`, pnpm):**
- `dev`: Generate meta artifact + start Vite dev server
- `build`: Generate meta artifact + typecheck + Vite build
- `generate-meta`: Generate the meta artifact only
- **Testing:** No formal unit testing framework. Validation occurs through:
- Script-based validation of every `blueprints/<id>/meta.json` (`build-scripts/generate-meta.js --check`)
- Manual testing via Dokploy preview deployments (import base64 from PR preview)
- TypeScript compilation errors caught during build
- ESLint for code quality in frontend
- **CI/CD Process:**
- **On blueprint changes:** Validates each template's metadata (required fields, id/folder match, logo file exists) and rejects a committed root `meta.json`.
- **On 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:** Template metadata lives in `blueprints/<id>/meta.json` (one object per template). Never create or edit a root-level `meta.json` — it is generated at build time. Run `node build-scripts/generate-meta.js --check` after any metadata edits.
- Test templates in Dokploy preview before submitting PRs (use base64 import from PR preview).
- Add logo file (SVG preferred, ~128x128px) to blueprint folder.
- Ensure the `id` in `blueprints/<id>/meta.json` exactly matches the blueprint folder name (lowercase kebab-case).
- **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}`
- **Volume bind mounts (in docker-compose.yml):** When mounting host paths, do NOT use absolute paths like `"/folder:/path/in/container"`. Use relative paths instead:
```yaml
# Invalid
volumes:
- "/folder:/path/in/container" ❌
# Valid
volumes:
- "../files/my-database:/var/lib/mysql" ✅
- "../files/my-configs:/etc/my-app/config" ✅
```
- **meta.json Requirements:**
- **Required fields:** `id`, `name`, `version`, `description`, `links` (with `github`), `logo`, `tags` (array)
- **Tags:** Lowercase strings (e.g., `["monitoring", "database"]`)
- **Version:** MUST match Docker image version in docker-compose.yml
- **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
-213
View File
@@ -1,213 +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.
### Pull Request Guidelines
- **Keep PRs small and focused.** Avoid very large PRs; prefer several smaller PRs (e.g., one template or one logical change per PR). This speeds up review and keeps the history clear.
- **Test before submitting.** Any PR that has not been tested by the contributor will be closed. This keeps the PR queue tidy and ensures that only contributions that have been verified by their authors are considered.
## Adding a New Template
To add a new template, follow these steps:
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. **Add `meta.json` in your template folder**: Create `blueprints/<id>/meta.json` with a single JSON object describing your template. Do NOT create or edit a root-level `meta.json` — the global registry is generated automatically at build time from every `blueprints/<id>/meta.json` (this is what makes template PRs conflict-free). Include:
- `id`: Folder name (e.g., "grafana") — must match the folder exactly.
- `name`: Display name.
- `version`: Image/tag version.
- `description`: Brief overview.
- `logo`: Filename of the logo (e.g., "grafana.svg").
- `links`: Object with `github`, `website`, `docs` (URLs; use `""` if not applicable).
- `tags`: Array of categories (e.g., ["monitoring"]).
- Example (`blueprints/grafana/meta.json`):
```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 build-scripts/generate-meta.js --check` to validate every template's metadata (same check CI runs), and manually check for syntax errors in YAML/TOML.
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 the template's `blueprints/<id>/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! 🚀
+47 -127
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`)
4. Add the template metadata (name, description, version, logo, links, tags) in a `meta.json` file inside your template folder (`blueprints/<id>/meta.json`) — do NOT create or edit a root-level `meta.json`, it is generated at build time
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 the meta information in `blueprints/<id>/meta.json` (a single JSON object; the root `meta.json` is generated automatically at build time)
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.
Each template's metadata lives in `blueprints/<id>/meta.json`; the global `meta.json` is generated at build time. Use `node build-scripts/generate-meta.js --check` to validate all template metadata.
+30 -34
View File
@@ -4,53 +4,49 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "node ../build-scripts/generate-meta.js && vite",
"build": "node ../build-scripts/generate-meta.js && tsc -b && vite build",
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
"generate-meta": "node ../build-scripts/generate-meta.js"
"preview": "vite preview"
},
"dependencies": {
"@codemirror/autocomplete": "^6.19.1",
"@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
-4
View File
@@ -1,4 +0,0 @@
packages:
- .
allowBuilds:
esbuild: false
-1
View File
@@ -1 +0,0 @@
meta.json
-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>
);
}
+2 -4
View File
@@ -13,9 +13,7 @@ const Navigation = () => {
"https://api.github.com/repos/dokploy/dokploy"
);
const data = await response.json();
setGithubStars(
Number.isFinite(data.stargazers_count) ? data.stargazers_count : 0
);
setGithubStars(data.stargazers_count);
} catch (error) {
console.error("Error fetching GitHub stars:", error);
}
@@ -34,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>
) : (
+3 -40
View File
@@ -14,7 +14,6 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs";
import { Label } from "./ui/label";
import { Clipboard } from "lucide-react";
import { Input } from "./ui/input";
import Markdown from "./ui/markdown";
interface Template {
id: string;
@@ -33,7 +32,6 @@ interface Template {
interface TemplateFiles {
dockerCompose: string | null;
config: string | null;
instructions: string | null;
}
interface TemplateDialogProps {
@@ -57,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 (
@@ -171,23 +167,8 @@ const TemplateDialog: React.FC<TemplateDialogProps> = ({
</div>
</div>
)}
<Tabs
defaultValue={
templateFiles?.instructions
? "instructions"
: "docker-compose"
}
className="w-full"
>
<Tabs defaultValue="docker-compose" className="w-full">
<TabsList className="w-full justify-start">
{templateFiles?.instructions && (
<TabsTrigger
value="instructions"
className="data-[state=active]:font-bold"
>
Instructions
</TabsTrigger>
)}
<TabsTrigger
value="docker-compose"
className="data-[state=active]:font-bold"
@@ -201,24 +182,6 @@ const TemplateDialog: React.FC<TemplateDialogProps> = ({
Configuration
</TabsTrigger>
</TabsList>
{templateFiles?.instructions && (
<TabsContent value="instructions" className="mt-4">
<div className="space-y-2">
<Label className="flex flex-col items-start w-fit justify-start gap-1">
<span className="leading-tight text-xl font-semibold">
Setup Instructions
</span>
<span className="leading-tight text-sm text-gray-500">
instructions.md
</span>
</Label>
<div className="rounded-md border p-4">
<Markdown content={templateFiles.instructions} />
</div>
</div>
</TabsContent>
)}
<TabsContent value="docker-compose" className="mt-4">
{templateFiles?.dockerCompose && (
<div className="space-y-2">
@@ -265,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
+7 -21
View File
@@ -28,7 +28,6 @@ interface Template {
interface TemplateFiles {
dockerCompose: string | null;
config: string | null;
instructions: string | null;
}
interface TemplateGridProps {
@@ -81,34 +80,20 @@ const TemplateGrid: React.FC<TemplateGridProps> = ({ view }) => {
const fetchTemplateFiles = async (templateId: string) => {
setModalLoading(true);
try {
const [dockerComposeRes, configRes, instructionsRes] = await Promise.all(
[
fetch(`/blueprints/${templateId}/docker-compose.yml`),
fetch(`/blueprints/${templateId}/template.toml`),
fetch(`/blueprints/${templateId}/instructions.md`),
]
);
const [dockerComposeRes, configRes] = await Promise.all([
fetch(`/blueprints/${templateId}/docker-compose.yml`),
fetch(`/blueprints/${templateId}/template.yml`),
]);
const dockerCompose = dockerComposeRes.ok
? await dockerComposeRes.text()
: null;
const config = configRes.ok ? await configRes.text() : null;
// Guard against SPA fallbacks that return index.html with a 200 status
// for templates that don't have an instructions.md file.
const instructionsIsMarkdown =
instructionsRes.ok &&
!(instructionsRes.headers.get("content-type") || "").includes(
"text/html"
);
const instructions = instructionsIsMarkdown
? await instructionsRes.text()
: null;
setTemplateFiles({ dockerCompose, config, instructions });
setTemplateFiles({ dockerCompose, config });
} catch (err) {
console.error("Error fetching template files:", err);
setTemplateFiles({ dockerCompose: null, config: null, instructions: null });
setTemplateFiles({ dockerCompose: null, config: null });
} finally {
setModalLoading(false);
}
@@ -135,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;
}
-177
View File
@@ -1,177 +0,0 @@
import React from "react";
// Minimal markdown renderer (no external dependencies) for the per-template
// instructions.md files. Supports headings, paragraphs, fenced code blocks,
// ordered/unordered lists, and inline code / bold / links.
const INLINE_PATTERN =
/(`[^`]+`)|(\*\*[^*]+\*\*)|(\[[^\]]+\]\([^)\s]+\))/g;
const renderInline = (text: string): React.ReactNode[] => {
const nodes: React.ReactNode[] = [];
let lastIndex = 0;
let key = 0;
let match: RegExpExecArray | null;
INLINE_PATTERN.lastIndex = 0;
while ((match = INLINE_PATTERN.exec(text)) !== null) {
if (match.index > lastIndex) {
nodes.push(text.slice(lastIndex, match.index));
}
const token = match[0];
if (token.startsWith("`")) {
nodes.push(
<code
key={key++}
className="px-1.5 py-0.5 rounded bg-muted font-mono text-[0.85em]"
>
{token.slice(1, -1)}
</code>
);
} else if (token.startsWith("**")) {
nodes.push(<strong key={key++}>{token.slice(2, -2)}</strong>);
} else {
const link = /^\[([^\]]+)\]\(([^)\s]+)\)$/.exec(token);
if (link) {
nodes.push(
<a
key={key++}
href={link[2]}
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 dark:text-blue-400 underline underline-offset-2"
>
{link[1]}
</a>
);
} else {
nodes.push(token);
}
}
lastIndex = match.index + token.length;
}
if (lastIndex < text.length) {
nodes.push(text.slice(lastIndex));
}
return nodes;
};
const HEADING_CLASSES: Record<number, string> = {
1: "text-2xl font-bold mt-2",
2: "text-xl font-semibold mt-6 border-b pb-1",
3: "text-lg font-semibold mt-4",
4: "text-base font-semibold mt-3",
};
interface MarkdownProps {
content: string;
}
const Markdown: React.FC<MarkdownProps> = ({ content }) => {
const lines = content.replace(/\r\n/g, "\n").split("\n");
const blocks: React.ReactNode[] = [];
let key = 0;
let i = 0;
while (i < lines.length) {
const line = lines[i];
// Blank line
if (line.trim() === "") {
i++;
continue;
}
// Fenced code block
if (line.trim().startsWith("```")) {
const code: string[] = [];
i++;
while (i < lines.length && !lines[i].trim().startsWith("```")) {
code.push(lines[i]);
i++;
}
i++; // skip closing fence
blocks.push(
<pre
key={key++}
className="bg-muted rounded-md p-4 overflow-x-auto text-sm font-mono whitespace-pre"
>
<code>{code.join("\n")}</code>
</pre>
);
continue;
}
// Heading
const heading = /^(#{1,4})\s+(.*)$/.exec(line);
if (heading) {
const level = heading[1].length;
const Tag = `h${Math.min(level + 1, 6)}` as keyof React.JSX.IntrinsicElements;
blocks.push(
<Tag key={key++} className={HEADING_CLASSES[level]}>
{renderInline(heading[2])}
</Tag>
);
i++;
continue;
}
// Unordered list
if (/^[-*]\s+/.test(line)) {
const items: string[] = [];
while (i < lines.length && /^[-*]\s+/.test(lines[i])) {
items.push(lines[i].replace(/^[-*]\s+/, ""));
i++;
}
blocks.push(
<ul key={key++} className="list-disc pl-6 space-y-1">
{items.map((item, idx) => (
<li key={idx}>{renderInline(item)}</li>
))}
</ul>
);
continue;
}
// Ordered list
if (/^\d+\.\s+/.test(line)) {
const items: string[] = [];
while (i < lines.length && /^\d+\.\s+/.test(lines[i])) {
items.push(lines[i].replace(/^\d+\.\s+/, ""));
i++;
}
blocks.push(
<ol key={key++} className="list-decimal pl-6 space-y-1">
{items.map((item, idx) => (
<li key={idx}>{renderInline(item)}</li>
))}
</ol>
);
continue;
}
// Paragraph: consume consecutive plain lines
const paragraph: string[] = [line];
i++;
while (
i < lines.length &&
lines[i].trim() !== "" &&
!/^(#{1,4})\s+/.test(lines[i]) &&
!/^[-*]\s+/.test(lines[i]) &&
!/^\d+\.\s+/.test(lines[i]) &&
!lines[i].trim().startsWith("```")
) {
paragraph.push(lines[i]);
i++;
}
blocks.push(
<p key={key++} className="leading-relaxed">
{renderInline(paragraph.join(" "))}
</p>
);
}
return <div className="space-y-3 text-sm">{blocks}</div>;
};
export default Markdown;
+4 -2
View File
@@ -11,9 +11,11 @@ export default defineConfig({
{
src: '../blueprints/*',
dest: 'blueprints' // raíz de dist (public root)
},
{
src: '../meta.json',
dest: '' // raíz de dist
}
// meta.json is no longer copied from the repo root: it is
// generated into app/public/meta.json by build-scripts/generate-meta.js
]
})
-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

-16
View File
@@ -1,16 +0,0 @@
{
"id": "ackee",
"name": "Ackee",
"version": "latest",
"description": "Ackee is a self-hosted analytics tool for your website.",
"logo": "logo.png",
"links": {
"github": "https://github.com/electerious/Ackee",
"website": "https://ackee.electerious.com/",
"docs": "https://docs.ackee.electerious.com/"
},
"tags": [
"analytics",
"self-hosted"
]
}
-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}"
-17
View File
@@ -1,17 +0,0 @@
{
"id": "activepieces",
"name": "Activepieces",
"version": "0.35.0",
"description": "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.",
"logo": "activepieces.svg",
"links": {
"github": "https://github.com/activepieces/activepieces",
"website": "https://www.activepieces.com/",
"docs": "https://www.activepieces.com/docs"
},
"tags": [
"automation",
"workflow",
"no-code"
]
}
-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: []
-17
View File
@@ -1,17 +0,0 @@
{
"id": "actualbudget",
"name": "Actual Budget",
"version": "latest",
"description": "A super fast and privacy-focused app for managing your finances.",
"logo": "actualbudget.png",
"links": {
"github": "https://github.com/actualbudget/actual",
"website": "https://actualbudget.org",
"docs": "https://actualbudget.org/docs"
},
"tags": [
"budgeting",
"finance",
"money"
]
}
-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

-18
View File
@@ -1,18 +0,0 @@
{
"id": "adguardhome",
"name": "AdGuard Home",
"version": "latest",
"description": "AdGuard Home is a comprehensive solution designed to enhance your online browsing experience by eliminating all kinds of ads, from annoying banners and pop-ups to intrusive video ads. It provides privacy protection, browsing security, and parental control features while maintaining website functionality.",
"logo": "logo.svg",
"links": {
"github": "https://github.com/AdguardTeam/AdGuardHome",
"website": "https://adguard.com",
"docs": "https://github.com/AdguardTeam/AdGuardHome/wiki"
},
"tags": [
"privacy",
"security",
"dns",
"ad-blocking"
]
}
-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

-18
View File
@@ -1,18 +0,0 @@
{
"id": "adminer",
"name": "Adminer",
"version": "4.8.1",
"description": "Adminer is a comprehensive database management tool that supports MySQL, MariaDB, PostgreSQL, SQLite, MS SQL, Oracle, Elasticsearch, MongoDB and others. It provides a clean interface for efficient database operations, with strong security features and extensive customization options.",
"logo": "logo.svg",
"links": {
"github": "https://github.com/vrana/adminer",
"website": "https://www.adminer.org/",
"docs": "https://www.adminer.org/en/plugins/"
},
"tags": [
"databases",
"developer-tools",
"mysql",
"postgresql"
]
}
-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: {}
-19
View File
@@ -1,19 +0,0 @@
{
"id": "adventurelog",
"name": "AdventureLog",
"version": "latest",
"description": "AdventureLog is an open-source activity tracker with maps, journaling, and Strava integration.",
"logo": "adventurelog.svg",
"links": {
"github": "https://github.com/seanmorley15/adventurelog",
"website": "https://adventurelog.app/",
"docs": "https://adventurelog.app/docs/intro/adventurelog_overview.html"
},
"tags": [
"activity",
"maps",
"django",
"react",
"postgres"
]
}
-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"
-84
View File
@@ -1,84 +0,0 @@
version: "3.8"
services:
affinepro:
image: ghcr.io/toeverything/affine:stable
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_INDEXER_ENABLED=false
- 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:
condition: service_healthy
redis:
condition: service_healthy
migration:
condition: service_completed_successfully
migration:
image: ghcr.io/toeverything/affine:stable
command: ["sh", "-c", "node ./scripts/self-host-predeploy.js"]
environment:
- REDIS_SERVER_HOST=redis
- REDIS_SERVER_PASSWORD=${REDIS_PASSWORD}
- DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db:5432/affinepro
- AFFINE_INDEXER_ENABLED=false
- AFFINE_SERVER_HOST=${DOMAIN}
- MAILER_HOST=${MAILER_HOST}
- MAILER_PORT=${MAILER_PORT}
- 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:
condition: service_healthy
redis:
condition: service_healthy
db:
image: pgvector/pgvector:pg16
restart: unless-stopped
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=affinepro
- POSTGRES_INITDB_ARGS=--data-checksums
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "postgres", "-d", "affinepro"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
restart: unless-stopped
command: redis-server --requirepass ${REDIS_PASSWORD}
volumes:
- redis-data:/data
healthcheck:
test: ["CMD-SHELL", "redis-cli -a '${REDIS_PASSWORD}' ping | grep PONG"]
interval: 10s
timeout: 5s
retries: 5
volumes:
affine-storage: {}
affine-config: {}
postgres-data: {}
redis-data: {}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

-18
View File
@@ -1,18 +0,0 @@
{
"id": "affinepro",
"name": "Affine Pro",
"version": "stable",
"description": "Affine Pro is a modern, self-hosted platform designed for collaborative content creation and project management. It offers an intuitive interface, seamless real-time collaboration, and powerful tools for organizing tasks, notes, and ideas.",
"logo": "logo.png",
"links": {
"github": "https://github.com/toeverything/Affine",
"website": "https://affine.pro/",
"docs": "https://docs.affine.pro/"
},
"tags": [
"collaboration",
"self-hosted",
"productivity",
"project-management"
]
}
-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}"
-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" id="a" viewBox="0 0 3271 1440"><defs><style>.st0{fill:#dedede}</style></defs><path d="M1009.95 1209.53c-125.1-214.98-250.74-430.9-378.92-651.16-126.82 219.73-251.81 436.28-376.62 652.51H60.69c190.42-329.11 570.87-981.75 570.87-981.75s382.11 651.12 572.49 980.41h-194.1Z" class="st0"/><path d="M875.46 1210.46H383.92c32.72-56.91 64.4-111.99 95.81-166.62H781.8c30.62 54.46 61.09 108.66 93.67 166.62zm1622.92-202.23c19.52 29.84 37.9 57.94 58.46 89.39h-63.61c-18.29-24.88-38.17-51.91-57.71-78.49h-72.24v78.17h-51.11V828.44c30.03 0 58.71-.22 87.38.06 23.48.24 47.32-1.26 70.37 2.21 37.67 5.66 66.94 25.71 74.9 64.9 8.22 40.54 1.62 78.35-35.52 104.62-3.14 2.22-6.21 4.54-10.93 7.99h.01Zm-135.01-40.37c34 0 66.71 1.87 99.08-.66 21.47-1.67 30.97-17.92 31.56-39.77.61-22.88-9.01-40.56-30.99-42.73-32.8-3.23-66.15-.84-99.65-.84zM1674.39 829.9v38.37c-52.36 58.82-104.25 117.13-160.14 179.91h158.93v49.49h-227.62v-51.9c46.98-52.11 95.02-105.41 147.93-164.09h-147.28V829.9zm906.29-489.95c64.48 63.91 125.11 123.99 189.35 187.65V373.68h47.4v271.19c-64.19-62.94-125.05-122.59-190.04-186.3v162.44h-46.72V339.94h.01Zm195.02 692.31c13.79-6.85 33.51-15.76 45.41-21.65 41.44 55.26 105.07 42.53 132.33 18s35.44-59.98 25.2-95.47c-13.36-34.33-42.97-61.14-86.36-59.62-43.38 1.52-81.55 32.41-85.2 84.34h-50.32c-.57-50.11 36.27-109.35 94.12-126.75 72.77-21.89 144.89 13.23 172.07 83.77 25.18 65.37-5.74 138.6-72.21 171-60.69 29.59-140.17 6.97-175.1-53.61h.04Zm-811.82-622.07c-11.07 8.4-24.55 17.68-34.13 24.95-55.99-46.33-119.62-30.88-142.33 27.28-13.63 38.17 6.27 88.2 48.2 103.62 36.45 13.4 76.82-.61 87.32-31.48-8.66-2.61-17.29-5.23-28.25-8.54-.93-12.84-1.85-25.67-2.96-40.9h96.82c7.25 56.02-37.39 118.12-94.41 132.17-63.06 15.54-129.35-19.2-152.31-79.83-22.64-59.76 4.81-131.25 61.88-161.16 55.93-29.3 126.75-15.37 160.17 33.88Zm-531.9-55.25c48.2 92.28 93.04 178.13 139.88 267.84h-53.47c-27.89-50.55-56.47-102.34-86.73-157.13-28.93 54.29-56.1 105.29-83.82 157.31h-54.54c45.87-88.68 90.79-175.5 138.65-268.02h.01Zm1642.09 60.65h-89.98v-46.22h226.22v46.24h-86.43v205.91h-49.82V415.59zM1947.89 878.34v65.77l63.09.91s-.03 30.03-.03 50.34c-25.69.89-53.1.45-78.35-5.53-21.38-5.05-36.73-22.63-37.67-46.05-1.5-36.98-.4-75.88-.4-115.24h216.51v49.81h-163.16Zm-52.88 218.57v-68.62c14.11-.65 27.22-1.25 40.97-1.87 3.47 7 3.06 19.81 9.96 20.09 6.88.29 107.67 0 164.04 0v50.41h-214.97Zm342.4-678.53v60.83l58.36.84s-.03 27.78-.03 46.57c-23.85.41-49.12.41-72.49-5.11-19.77-4.68-33.97-20.93-34.85-42.59-1.39-34.2-.37-70.19-.37-106.6h200.29v46.07H2237.4h.01Zm-48.91 202.2v-63.49c13.05-.6 25.18-1.15 37.9-1.73 3.2 6.48 2.83 18.32 9.2 18.59s99.6 0 151.75 0v46.63h-198.87.01Z" class="st0"/></svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

-14
View File
@@ -1,14 +0,0 @@
version: "3.8"
services:
agent-zero:
image: agent0ai/agent-zero:v1.9
restart: unless-stopped
expose:
- "80"
environment:
- AUTH_LOGIN=${AUTH_LOGIN}
- AUTH_PASSWORD=${AUTH_PASSWORD}
volumes:
- agent-zero-data:/a0/usr
volumes:
agent-zero-data:
-17
View File
@@ -1,17 +0,0 @@
{
"id": "agent-zero",
"name": "Agent Zero",
"version": "v1.9",
"description": "Agent Zero is an open-source autonomous AI agent framework with memory and tool use capabilities.",
"logo": "agent-zero.svg",
"links": {
"github": "https://github.com/agent0ai/agent-zero",
"website": "https://www.agent-zero.ai",
"docs": "https://www.agent-zero.ai/p/docs/"
},
"tags": [
"ai",
"agent",
"automation"
]
}
-16
View File
@@ -1,16 +0,0 @@
[variables]
main_domain = "${domain}"
auth_login = "${username}"
auth_password = "${password:32}"
[config]
env = [
"AUTH_LOGIN=${auth_login}",
"AUTH_PASSWORD=${auth_password}",
]
mounts = []
[[config.domains]]
serviceName = "agent-zero"
port = 80
host = "${main_domain}"
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: {}
-20
View File
@@ -1,20 +0,0 @@
{
"id": "agentdvr",
"name": "Agent DVR",
"version": "latest",
"description": "Agent DVR is a comprehensive video surveillance software with motion detection, alerts, and remote access capabilities.",
"logo": "agentdvr.png",
"links": {
"github": "https://github.com/ispysoftware/AgentDVR",
"website": "https://www.ispyconnect.com/",
"docs": "https://www.ispyconnect.com/userguide-agent-dvr.aspx"
},
"tags": [
"surveillance",
"security",
"video",
"monitoring",
"dvr",
"camera"
]
}
-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

-19
View File
@@ -1,19 +0,0 @@
{
"id": "akaunting",
"name": "Akaunting",
"version": "latest",
"description": "Akaunting is a self-hosted, open-source accounting app for small businesses.",
"logo": "image.png",
"links": {
"github": "https://github.com/akaunting/akaunting",
"website": "https://akaunting.com",
"docs": "https://akaunting.com/docs"
},
"tags": [
"finance",
"accounting",
"php",
"mariadb",
"self-hosted"
]
}
-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]]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

-30
View File
@@ -1,30 +0,0 @@
services:
alarik:
image: ghcr.io/achtungsoftware/alarik:latest
restart: unless-stopped
volumes:
- alarik-storage:/app/Storage
environment:
- API_BASE_URL=${API_BASE_URL}
- CONSOLE_BASE_URL=${CONSOLE_BASE_URL}
- ADMIN_USERNAME=${ADMIN_USERNAME}
- ADMIN_PASSWORD=${ADMIN_PASSWORD}
- JWT=${JWT}
- ALLOW_ACCOUNT_CREATION=${ALLOW_ACCOUNT_CREATION}
expose:
- 8080
console:
image: ghcr.io/achtungsoftware/alarik-console:latest
restart: unless-stopped
environment:
- NUXT_PUBLIC_API_BASE_URL=${API_BASE_URL}
- NUXT_PUBLIC_CONSOLE_BASE_URL=${CONSOLE_BASE_URL}
- NUXT_PUBLIC_ALLOW_ACCOUNT_CREATION=${ALLOW_ACCOUNT_CREATION}
expose:
- 3000
depends_on:
- alarik
volumes:
alarik-storage:
-17
View File
@@ -1,17 +0,0 @@
{
"id": "alarik",
"name": "Alarik",
"description": "Alarik is a high-performance, S3-compatible object storage.",
"logo": "alarik.png",
"version": "latest",
"links": {
"github": "https://github.com/achtungsoftware/alarik",
"website": "https://alarik.io/",
"docs": "https://alarik.io/docs"
},
"tags": [
"storage",
"s3",
"object-storage"
]
}
-26
View File
@@ -1,26 +0,0 @@
[variables]
console_domain = "${domain}"
api_domain = "${domain}"
[config]
mounts = []
# S3-compatible API - point AWS CLI / SDKs / rclone / backup tools at this domain
[[config.domains]]
serviceName = "alarik"
port = 8_080
host = "${api_domain}"
# Web management console - log in with the generated admin credentials below
[[config.domains]]
serviceName = "console"
port = 3_000
host = "${console_domain}"
[config.env]
API_BASE_URL = "http://${api_domain}"
CONSOLE_BASE_URL = "http://${console_domain}"
ADMIN_USERNAME = "alarik"
ADMIN_PASSWORD = "${password:16}"
JWT = "${base64:64}"
ALLOW_ACCOUNT_CREATION = "false"
+1 -1
View File
@@ -1,7 +1,7 @@
version: '3.3'
services:
alist:
image: xhofe/alist:v3.55.0
image: xhofe/alist:v3.41.0
volumes:
- alist-data:/opt/alist/data
environment:
-17
View File
@@ -1,17 +0,0 @@
{
"id": "alist",
"name": "AList",
"version": "v3.55.0",
"description": "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.",
"logo": "alist.svg",
"links": {
"github": "https://github.com/AlistGo/alist",
"website": "https://alistgo.com/",
"docs": "https://alistgo.com/guide/install/docker.html"
},
"tags": [
"file",
"webdav",
"storage"
]
}
-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

-17
View File
@@ -1,17 +0,0 @@
{
"id": "alltube",
"name": "AllTube",
"version": "latest",
"description": "AllTube Download is an application designed to facilitate the downloading of videos from YouTube and other video sites. It provides an HTML GUI for youtube-dl with video conversion capabilities and JSON API support.",
"logo": "logo.png",
"links": {
"github": "https://github.com/Rudloff/alltube",
"website": "https://github.com/Rudloff/alltube",
"docs": "https://github.com/Rudloff/alltube/wiki"
},
"tags": [
"media",
"video",
"downloader"
]
}
-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

-17
View File
@@ -1,17 +0,0 @@
{
"id": "ampache",
"name": "Ampache",
"version": "latest",
"description": "Ampache is a web-based audio/video streaming application and file manager allowing you to access your music & videos from anywhere, using almost any internet enabled device.",
"logo": "logo.png",
"links": {
"github": "https://github.com/ampache/ampache",
"website": "http://ampache.org/",
"docs": "https://github.com/ampache/ampache/wiki"
},
"tags": [
"media",
"music",
"streaming"
]
}
-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 @@
{
"id": "anonupload",
"name": "AnonUpload",
"version": "1",
"description": "AnonUpload is a secure, anonymous file sharing application that does not require a database. It is built with privacy as a priority, ensuring that the direct filename used is not displayed.",
"logo": "logo.png",
"links": {
"github": "https://github.com/supernova3339/anonupload",
"docs": "https://github.com/Supernova3339/anonupload/blob/main/env.md",
"website": "https://anonupload.com/"
},
"tags": [
"file-sharing",
"privacy"
]
}
-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 @@
{
"id": "anse",
"name": "Anse",
"version": "latest",
"description": "Anse is an open-source alternative to ChatGPT web UI, supporting OpenAI-compatible APIs.",
"logo": "image.png",
"links": {
"github": "https://github.com/ddiu8081/anse",
"website": "https://anse.app/",
"docs": "https://github.com/ddiu8081/anse#readme"
},
"tags": [
"ai",
"chatbot",
"openai",
"ui"
]
}
-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"
-16
View File
@@ -1,16 +0,0 @@
{
"id": "answer",
"name": "Answer",
"version": "v1.4.1",
"description": "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.",
"logo": "answer.png",
"links": {
"github": "https://github.com/apache/answer",
"website": "https://answer.apache.org/",
"docs": "https://answer.apache.org/docs"
},
"tags": [
"q&a",
"self-hosted"
]
}
-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
-16
View File
@@ -1,16 +0,0 @@
{
"id": "anubis",
"name": "Anubis",
"version": "latest",
"description": "Anubis is a bot protector, It will block bots from accessing your website.",
"logo": "anubis.webp",
"links": {
"github": "https://github.com/TecharoHQ/anubis",
"website": "https://anubis.techaro.lol",
"docs": "https://anubis.techaro.lol/docs/"
},
"tags": [
"self-hosted",
"bot-protection"
]
}
-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

-17
View File
@@ -1,17 +0,0 @@
{
"id": "anythingllm",
"name": "AnythingLLM",
"version": "latest",
"description": "AnythingLLM is a private, self-hosted, and local document chatbot platform that allows you to chat with your documents using various LLM providers.",
"logo": "logo.png",
"links": {
"github": "https://github.com/Mintplex-Labs/anything-llm",
"website": "https://anythingllm.com/",
"docs": "https://docs.anythingllm.com/"
},
"tags": [
"ai",
"llm",
"chatbot"
]
}

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