Compare commits

...
Author SHA1 Message Date
Mauricio SiuandClaude Opus 5.5 3fb632006a fix(orbit): give storage its own generated domain
"storage-${domain}" pushes the first DNS label of Dokploy's generated
sslip.io hosts past 63 characters, so the storage domain never resolves
and browser uploads can't reach it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 16:59:42 -06:00
Mauricio SiuandClaude Opus 5.5 63b0eef948 fix(orbit): replace MinIO with SeaweedFS S3 storage
MinIO images on quay.io/Docker Hub are no longer publicly pullable, so the
deploy failed at image pull. Storage now runs SeaweedFS (S3 on port 9000,
credentials orbit/MINIO_PASSWORD) and the bucket service uses mc from the
orbit-bucket image to create orbit-uploads and set its CORS rule for the
app origin (browser presigned PUT uploads).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:19:35 -06:00
shashank agarwal e429bff0c2 fix(orbit): use tested versioned multi-platform images 2026-09-24 13:32:24 +05:30
shashank agarwal 6425052f98 fix: support generated storage passwords 2026-09-24 01:41:50 +05:30
shashank agarwal 62c2f0ed73 feat: add Orbit task management template 2026-09-24 01:20:22 +05:30
830d6bbc8d Add OpenStatus template (#1126)
Self-hosted OpenStatus (status pages + uptime monitoring) following the
official docker-compose.github-packages.yaml setup: local libSQL database,
one-shot migration runner built from the pinned upstream commit, workflows,
API server, dashboard, status-page, and the private-location ingest server.
All app images pinned to upstream commit e1a9825; Tinybird analytics run in
no-op mode by default.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-31 14:05:48 -06:00
Ghazi Ben DahmaneandGitHub b87d9a9a8a feat: add Rustrak SQLite full-stack template (#1115) 2026-08-30 17:46:06 -06:00
Ghazi Ben DahmaneandGitHub a9aa64ee51 feat: add FalkorDB template (#1116) 2026-08-30 17:45:24 -06:00
Ghazi Ben DahmaneandGitHub 8ac27a0f89 fix: update Pocket ID template to v2.14.0 (#1113) 2026-08-30 17:44:43 -06:00
614e4fef12 fix(odoo_17, odoo_18): provide odoo.conf via config mount and enable proxy_mode (#1124)
Same root cause as odoo_19 (#1108, fixed in #1110): the compose file
bind-mounts ../files/config over /etc/odoo, but with mounts = [] the
directory is empty, hiding the image's /etc/odoo/odoo.conf. As a result
wait-for-psql.py crashes with configparser.NoSectionError: No section:
'options' and the web container never starts.

Ship odoo.conf through the template config mount instead, with
proxy_mode enabled so websocket/POS/livechat work behind Traefik.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 16:45:14 -06:00
c784f9b9c8 feat: add Silo object storage service with Docker Compose setup (#1112)
* feat: add Silo object storage service with Docker Compose setup

* update version to match docker image

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-08-30 16:35:58 -06:00
Steven TranandGitHub 698ab85f6d feat(template): add Quire Ink (#1117) 2026-08-30 16:35:18 -06:00
egedemirkaranandGitHub 6284bbc74f fix: Update outdated website URL (#1120) 2026-08-30 16:34:37 -06:00
7b1cf99803 fix(postiz): pass all social provider env vars and fix Temporal healthcheck (#1121)
* fix(postiz): pass all social provider env vars and fix Temporal healthcheck

* fix(postiz): drop env vars Postiz does not read, unset NOT_SECURED

- GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET are not read by Postiz:
  the Google auth provider and GMB both use YOUTUBE_CLIENT_ID
  (google.provider.ts, gmb.provider.ts).
- NEXT_PUBLIC_FRONTEND_URL does not exist in the Postiz codebase.
- Postiz checks NOT_SECURED for truthiness only, so the string
  "false" still enables insecure mode; remove the variable so
  secure cookies are actually on for the HTTPS deployment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Mauricio Siu <mauricio@dokploy.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 16:33:41 -06:00
1aa2d74f4e data permission right & Traefik configuration (#1110)
* data permission right & Traefik configuration

Permession to write on data directory is mandatory for odoo server to run
Added Traefik configuration to allow POS and live chat to run

* fix(odoo_19): provide odoo.conf via config mount and enable proxy_mode

The empty ../files/config bind mount was hiding the image's
/etc/odoo/odoo.conf, crashing wait-for-psql.py (NoSectionError:
'options') in a restart loop. Ship odoo.conf through the template
mount instead, with proxy_mode enabled so websocket/POS/livechat
work behind Traefik (threaded mode serves /websocket on 8069, so
no extra routing is needed). Revert the manual Traefik labels
(invalid compose: labels must be a mapping; Dokploy generates
routing from template.toml) and the chown command (the named
volume already gets odoo ownership from the image).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Mauricio Siu <mauricio@dokploy.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 16:33:00 -06:00
JavierandGitHub b35349963e feat(wupnext): add new env vars for mcp server (#1100)
* feat(wupnext): add new env vars for mcp server

* fix: set explicit docker image version tag
2026-08-30 16:21:07 -06:00
Bora GökçeandGitHub 127b607d76 feat: add BlogFactory template (#1111) 2026-08-30 16:20:26 -06:00
Manan SantokiandGitHub c974384957 feat: make Firecrawl template production-ready (#1118)
* feat: make Firecrawl template production-ready

* fix: generate valid Firecrawl API keys
2026-08-30 16:03:35 -06:00
b05ce42a12 chore(plausible): update to v3.2.1 (#1123)
- ghcr.io/plausible/community-edition v2.1.5 -> v3.2.1 (latest stable release)
- clickhouse-server 24.3.3.102-alpine -> 24.12-alpine, matching the official
  CE v3.2.1 compose.yml
- CLICKHOUSE_SKIP_USER_SETUP=1 on the events db: required by ClickHouse
  24.11+ images so Plausible can connect as the passwordless default user
  (without it the app crash-loops with AUTHENTICATION_FAILED)
- plausible-data volume + TMPDIR per the official v3 compose

Deploy-verified on a Dokploy instance: fresh import, all 3 containers
running, HTTP 200 on the domain with the Plausible login page.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-30 15:04:31 -06:00
Mauricio SiuandGitHub f29a0b7340 Merge pull request #1103 from jmaxdev/canary
feat: add Kotonoba template
2026-08-25 11:46:44 -06:00
Mauricio SiuandGitHub b5b37264a4 Merge pull request #1109 from Dokploy/chore/sync-main-into-canary
chore: sync main into canary (OpenSEO #1107)
2026-08-25 00:15:48 -06:00
Mauricio SiuandClaude Fable 5 39ce5301b4 fix(kotonoba): pin image to 1.0.19, ADMIN_DOMAIN as hostname
- ghcr.io/trixtylab/kotonoba:latest -> 1.0.19 (same digest as latest today); meta.json version 1.0.19.
- ADMIN_DOMAIN is documented upstream as a bare hostname, pass ${main_domain} without scheme.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 20:30:35 -06:00
jmaxdev d5d374ad72 feat: add Kotonoba template 2026-08-22 13:23:56 -03:00
54 changed files with 2875 additions and 203 deletions
+12
View File
@@ -0,0 +1,12 @@
# BlogFactory on Dokploy
Dokploy creates independent database, storage, JWT, encryption, and scheduler secrets during import. Only the `web` service receives a public domain; the API, scheduler, PostgreSQL, and MinIO remain private.
## First administrator
1. Open the Compose service's environment settings and copy the generated `ADMIN_EMAILS` value.
2. Open the generated BlogFactory domain and register with that exact email address.
3. Return to the Compose environment settings, change `BLOGFACTORY_ALLOW_SIGNUP` to `false`, and redeploy.
4. Add OpenRouter, CMS, or Google Search Console credentials from BlogFactory only when you need those optional integrations.
BlogFactory sends reviewed content to connected CMS providers as drafts. The template does not grant live-publish or delete authority.
+8
View File
@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
<rect width="64" height="64" rx="8" fill="#fff"/>
<rect x="1" y="1" width="62" height="62" rx="7" fill="none" stroke="#d7d8d4" stroke-width="2"/>
<circle cx="50" cy="14" r="5" fill="#ff5a1f"/>
<rect x="13" y="31" width="10" height="18" fill="#202224"/>
<rect x="27" y="20" width="10" height="29" fill="#202224"/>
<rect x="41" y="26" width="10" height="23" fill="#202224"/>
</svg>

After

Width:  |  Height:  |  Size: 453 B

+102
View File
@@ -0,0 +1,102 @@
version: "3.8"
services:
postgres:
image: postgres:17.6-alpine3.22
restart: unless-stopped
environment:
POSTGRES_DB: blogfactory
POSTGRES_USER: blogfactory
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U blogfactory -d blogfactory"]
interval: 5s
timeout: 5s
retries: 20
minio:
image: minio/minio:RELEASE.2025-09-07T16-13-09Z
restart: unless-stopped
command: server /data --console-address :9001
environment:
MINIO_ROOT_USER: ${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD}
volumes:
- minio-data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 5s
timeout: 5s
retries: 20
api:
image: ghcr.io/blogfactoryhq/blogfactory-api:v0.1.0
restart: unless-stopped
command: >-
sh -c "bun run src/init-s3-bucket.ts && bun run src/db/migrate.ts && exec bun run src/index.ts"
depends_on:
postgres:
condition: service_healthy
minio:
condition: service_healthy
expose:
- 3000
environment:
NODE_ENV: production
PORT: 3000
BLOGFACTORY_SELF_HOSTED: "true"
DATABASE_URL: postgresql://blogfactory:${POSTGRES_PASSWORD}@postgres:5432/blogfactory
JWT_SECRET: ${JWT_SECRET}
API_KEY_ENCRYPTION_SECRET: ${API_KEY_ENCRYPTION_SECRET}
ADMIN_EMAILS: ${ADMIN_EMAILS}
BLOGFACTORY_ALLOW_SIGNUP: ${BLOGFACTORY_ALLOW_SIGNUP}
CRON_SECRET: ${CRON_SECRET}
WEB_APP_URL: ${BLOGFACTORY_URL}
MCP_APP_URL: http://web/mcp-review.html
MCP_ALLOWED_ORIGINS: ${BLOGFACTORY_URL}
S3_ENDPOINT: http://minio:9000
S3_ACCESS_KEY_ID: ${MINIO_ROOT_USER}
S3_SECRET_ACCESS_KEY: ${MINIO_ROOT_PASSWORD}
S3_BUCKET: blogfactory
S3_REGION: us-east-1
healthcheck:
test: ["CMD", "bun", "-e", "fetch('http://localhost:3000/api/ready').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))"]
interval: 10s
timeout: 5s
retries: 20
web:
image: ghcr.io/blogfactoryhq/blogfactory-web:v0.1.0
restart: unless-stopped
depends_on:
api:
condition: service_healthy
expose:
- 80
environment:
API_UPSTREAM: http://api:3000
healthcheck:
test: ["CMD-SHELL", "nginx -t >/dev/null 2>&1 && wget -q -O /dev/null http://api:3000/api/ready"]
interval: 10s
timeout: 5s
retries: 20
scheduler:
image: curlimages/curl:8.15.0
restart: unless-stopped
depends_on:
api:
condition: service_healthy
environment:
CRON_SECRET: ${CRON_SECRET}
CRON_INTERVAL_SECONDS: 21600
entrypoint: ["/bin/sh", "-c"]
command:
- >-
while true; do curl --fail --silent --show-error --output /dev/null -H "Authorization: Bearer $${CRON_SECRET}" http://api:3000/api/cron/drain && echo "Scheduler drain completed"; sleep "$${CRON_INTERVAL_SECONDS}"; done
volumes:
postgres-data:
minio-data:
+13
View File
@@ -0,0 +1,13 @@
{
"id": "blogfactory",
"name": "BlogFactory",
"version": "v0.1.0",
"description": "Self-hosted control plane for AI-assisted content operations and human-approved CMS draft delivery.",
"logo": "blogfactory.svg",
"links": {
"github": "https://github.com/BlogFactoryHQ/blogfactory",
"website": "https://blogfactory.io/",
"docs": "https://github.com/BlogFactoryHQ/blogfactory/blob/main/docs/self-hosting.md"
},
"tags": ["ai", "automation", "content-management"]
}
+28
View File
@@ -0,0 +1,28 @@
[variables]
main_domain = "${domain}"
postgres_password = "${password:48}"
minio_root_user = "blogfactory"
minio_root_password = "${password:48}"
jwt_secret = "${base64:48}"
encryption_secret = "${base64:48}"
cron_secret = "${base64:48}"
admin_email = "${email}"
[config]
env = [
"POSTGRES_PASSWORD=${postgres_password}",
"MINIO_ROOT_USER=${minio_root_user}",
"MINIO_ROOT_PASSWORD=${minio_root_password}",
"JWT_SECRET=${jwt_secret}",
"API_KEY_ENCRYPTION_SECRET=${encryption_secret}",
"CRON_SECRET=${cron_secret}",
"ADMIN_EMAILS=${admin_email}",
"BLOGFACTORY_ALLOW_SIGNUP=true",
"BLOGFACTORY_URL=https://${main_domain}",
]
mounts = []
[[config.domains]]
serviceName = "web"
port = 80
host = "${main_domain}"
+37
View File
@@ -0,0 +1,37 @@
version: "3.8"
services:
falkordb:
image: falkordb/falkordb:v4.20.4-alpine
restart: unless-stopped
environment:
- REDIS_ARGS=--requirepass ${FALKORDB_PASSWORD} --appendonly yes
- FALKORDB_ARGS=MAX_QUEUED_QUERIES 25 TIMEOUT 1000 RESULTSET_SIZE 10000 IMPORT_FOLDER /app/.data/import
- FALKORDB_PASSWORD=${FALKORDB_PASSWORD}
- AUTH_URL=${AUTH_URL}
- AUTH_SECRET=${AUTH_SECRET}
- NEXTAUTH_URL=${AUTH_URL}
- NEXTAUTH_SECRET=${AUTH_SECRET}
- ENCRYPTION_KEY=${ENCRYPTION_KEY}
- ALLOWED_ORIGINS=${AUTH_URL}
- TRUST_PROXY_HEADERS=true
- CSV_STORAGE=local
- CSV_LOCAL_TEMP_DIR=/app/.data/import
- CSV_LOCAL_LOAD_URI_MODE=file
- API_TOKEN_STORAGE_PATH=/app/.data/api_tokens.json
volumes:
- falkordb_data:/var/lib/falkordb/data
- falkordb_browser_data:/app/.data
expose:
- 3000
- 6379
healthcheck:
test: ["CMD-SHELL", "REDISCLI_AUTH=$$FALKORDB_PASSWORD redis-cli ping | grep PONG"]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
volumes:
falkordb_data:
falkordb_browser_data:
+17
View File
@@ -0,0 +1,17 @@
<svg width="508" height="508" viewBox="0 0 508 508" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1764_80)">
<rect x="2" width="508" height="508" rx="63.5" fill="#191919"/>
<path d="M459.448 247.263C459.06 249.273 458.295 251.265 457.465 253.036C456.174 255.775 454.477 258.357 452.439 260.598C447.809 265.671 441.271 268.253 434.576 269.608C427.88 270.964 407.573 270.088 399.089 268.041C377.481 262.821 355.182 262.941 341.966 268.05C333.583 271.296 330.651 273.15 326.224 276.968C308.711 292.027 300.872 328.455 293.919 349.897C289.188 364.56 284.549 379.823 273.925 391.378C257.565 409.149 230.332 414 205.635 414H97.1546C95.7989 414 94.7752 412.773 95.0427 411.436L97.588 398.35C99.6538 387.689 109.005 379.998 119.869 379.998H192.706C213.732 379.998 236.234 377.056 247.891 360.963C254.43 351.916 256.311 340.711 258.985 330.051C268.346 292.728 271.454 258.219 308.822 243.5C297.811 236.621 289.806 225.499 287.021 213.27C281.902 190.694 294.011 167.962 295.136 144.897C295.634 135.075 293.319 123.999 284.697 118.549C278.656 114.722 270.91 114.611 263.652 114.611C236.409 114.639 219.182 114.657 187.504 114.676H180.145C165.897 114.685 155.199 101.691 157.919 87.7101L158.685 83.7446C158.879 82.7209 159.745 82.0016 160.778 82.0016C177.637 82.0016 241.602 82.103 273.695 82.0016C292.222 81.9555 313.036 82.8224 325.579 95.7242C329.738 100.013 332.394 104.992 333.962 110.387C334.035 110.6 334.091 110.83 334.155 111.051C337.752 124.267 334.921 139.852 331.296 153.621C326.63 171.531 321.632 192.345 331.628 209.461C335.603 216.258 341.828 221.293 349.224 223.959C353.356 225.453 355.126 226.2 359.516 226.163C361.711 226.144 363.897 225.923 366.064 225.536C370.38 224.779 375.074 223.728 378.966 222.963C380.829 222.594 382.701 222.262 384.591 221.994C389.719 221.275 395.206 221.137 400.426 222.179C404.253 222.944 407.988 224.355 411.188 226.605C414.591 229.003 416.602 231.982 418.391 235.671C419.396 237.737 420.641 239.83 422.67 240.909C426.303 242.846 431.938 241.545 435.848 241.232C439.915 240.909 443.955 240.273 448.012 239.802C449.986 239.572 451.959 239.369 453.942 239.277C455.501 239.203 457.198 239.406 458.369 240.559C459.208 241.388 459.605 242.587 459.697 243.768C459.78 244.911 459.678 246.083 459.457 247.254L459.448 247.263Z" fill="url(#paint0_linear_1764_80)"/>
<path d="M182.763 183.28L172.627 235.164C172.36 236.556 173.421 237.848 174.841 237.848H229.159C230.58 237.848 231.64 239.139 231.373 240.531L228.956 253.055C226.909 263.66 217.622 271.333 206.823 271.333H167.509C166.43 271.333 165.499 272.099 165.296 273.159L154.663 327.847C152.606 338.443 143.319 346.088 132.529 346.088H110.525C109.105 346.088 108.045 344.797 108.312 343.405L145.902 149.794C146.104 148.734 147.036 147.968 148.115 147.968H254.096C255.516 147.968 256.577 149.259 256.309 150.652L253.893 163.176C251.846 173.781 242.559 181.454 231.76 181.454H184.985C183.906 181.454 182.975 182.219 182.772 183.28H182.763Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear_1764_80" x1="459.787" y1="82.0147" x2="70.7574" y2="381.599" gradientUnits="userSpaceOnUse">
<stop stop-color="#7466FF"/>
<stop offset="0.5" stop-color="#FF66B3"/>
<stop offset="1" stop-color="#FF804D"/>
</linearGradient>
<clipPath id="clip0_1764_80">
<rect width="508" height="508" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+4
View File
@@ -0,0 +1,4 @@
1. Open the generated domain to access FalkorDB Browser.
2. Create a connection using `localhost` as the host and `6379` as the port.
3. Leave the username empty and use the generated `FALKORDB_PASSWORD` from the Dokploy environment settings.
4. Keep port `6379` private unless remote database clients need direct access. The browser interface can access it inside the container.
+20
View File
@@ -0,0 +1,20 @@
{
"id": "falkordb",
"name": "FalkorDB",
"version": "4.20.4",
"description": "A high-performance graph database for knowledge graphs, GraphRAG, agent memory, fraud detection and other relationship-heavy workloads. Includes the FalkorDB Browser interface.",
"logo": "falkordb.svg",
"links": {
"github": "https://github.com/FalkorDB/FalkorDB",
"website": "https://www.falkordb.com/",
"docs": "https://docs.falkordb.com/"
},
"tags": [
"database",
"graph-database",
"knowledge-graph",
"graphrag",
"ai",
"redis"
]
}
+19
View File
@@ -0,0 +1,19 @@
[variables]
main_domain = "${domain}"
falkordb_url = "https://${main_domain}"
falkordb_password = "${hash:32}"
auth_secret = "${base64:48}"
encryption_key = "${hash:32}"
[config]
env = [
"FALKORDB_PASSWORD=${falkordb_password}",
"AUTH_URL=${falkordb_url}",
"AUTH_SECRET=${auth_secret}",
"ENCRYPTION_KEY=${encryption_key}",
]
[[config.domains]]
serviceName = "falkordb"
port = 3000
host = "${main_domain}"
File diff suppressed because one or more lines are too long
+101
View File
@@ -0,0 +1,101 @@
# Firecrawl
This template deploys Firecrawl `2.11.230` with authenticated API access, persistent infrastructure, browser rendering, and interactive Swagger documentation.
## First deployment
1. Create the template and keep the generated environment values unchanged.
2. Deploy the Compose service.
3. Wait for `firecrawl-db-seed` to finish and for `gateway` and `firecrawl` to become healthy. The first start can take several minutes.
4. Open `https://<your-domain>/docs/` for the interactive API documentation.
Dokploy generates the client credential in the Environment tab:
```text
FIRECRAWL_API_KEY=fc-...
```
Treat this value as a secret. The template provisions it in Firecrawl's PostgreSQL authentication database automatically.
## Test the API
```bash
curl -X POST "https://<your-domain>/v2/scrape" \
-H "Authorization: Bearer <FIRECRAWL_API_KEY>" \
-H "Content-Type: application/json" \
-d '{"url":"https://docs.dokploy.com","formats":[{"type":"markdown"}]}'
```
The OpenAPI document is available at:
```text
https://<your-domain>/openapi.json
```
## Included services
- `gateway`: public same-origin API and documentation gateway.
- `firecrawl`: API plus queue, extract, indexing, NuQ, and reconciliation workers.
- `playwright-service`: browser-rendered scraping.
- `firecrawl-db`: authentication, request, crawl, monitoring, and application data.
- `nuq-postgres`: durable NuQ job storage.
- `redis`: queues, caching, and rate-limit state.
- `rabbitmq`: extract-job transport.
- `swagger-ui`: interactive API documentation.
Only the gateway is assigned a public domain. PostgreSQL, Redis, RabbitMQ, Playwright, and worker ports remain private.
## Capabilities
Scrape, batch scrape, crawl, map, parsing, browser sessions, monitoring, change tracking, and the normal non-AI API paths work without an external model provider.
AI-backed extraction, JSON generation, agent features, and summaries require one of these configurations in the Environment tab:
- `OPENAI_API_KEY`, optionally with `OPENAI_BASE_URL`
- `OPENROUTER_API_KEY`
- `OLLAMA_BASE_URL`
Set `MODEL_NAME` and `MODEL_EMBEDDING_NAME` when the provider requires explicit model names.
Search integrations can be configured with `SEARCHAPI_API_KEY` or `SEARXNG_ENDPOINT`. Proxy variables and LlamaParse are optional.
## MCP clients
The Firecrawl API is not itself an MCP transport. Connect the official Firecrawl MCP adapter to this deployment:
```json
{
"mcpServers": {
"firecrawl-selfhost": {
"command": "npx",
"args": ["-y", "firecrawl-mcp"],
"env": {
"FIRECRAWL_API_URL": "https://<your-domain>",
"FIRECRAWL_API_KEY": "<FIRECRAWL_API_KEY>"
}
}
}
}
```
## Persistence and backups
The following named volumes contain persistent state:
- `firecrawl-db-data`
- `nuq-postgres-data`
- `redis-data`
- `rabbitmq-data`
Back up both PostgreSQL volumes before upgrading. Redis and RabbitMQ backups are also recommended when queued jobs must survive disaster recovery.
Database initialization scripts run only when `firecrawl-db-data` is empty. A newer Firecrawl image may require schema or RPC changes, so do not replace the pinned image with `latest` without reviewing migrations and testing a restored backup.
## Security notes
- Keep `ALLOW_LOCAL_WEBHOOKS=false` unless private-network webhook access is intentional.
- Keep the generated database, Redis, RabbitMQ, and Bull secrets private.
- Use HTTPS for the public domain.
- The generated API key receives unrestricted self-host rate-limit overrides; protect it like an administrator credential.
- Changing `FIRECRAWL_API_KEY` after deployment provisions another key and does not automatically revoke the old one.
+6 -4
View File
@@ -1,19 +1,21 @@
{
"id": "firecrawl",
"name": "Firecrawl",
"version": "latest",
"description": "Firecrawl is an API service that takes a URL, crawls it, and converts it into clean markdown or structured data. It can crawl all accessible subpages and provide clean data for each.",
"version": "2.11.230",
"description": "Production-ready, authenticated Firecrawl with persistent queues, browser rendering, a generated API key, and interactive OpenAPI documentation.",
"logo": "firecrawl.svg",
"links": {
"github": "https://github.com/firecrawl/firecrawl",
"website": "https://firecrawl.dev",
"docs": "https://github.com/firecrawl/firecrawl"
"docs": "https://docs.firecrawl.dev/contributing/self-host"
},
"tags": [
"api",
"crawler",
"scraping",
"data-extraction",
"llm"
"llm",
"automation",
"self-hosted"
]
}
+49 -53
View File
@@ -1,65 +1,61 @@
[variables]
main_domain = "${domain}"
openai_api_key = "${OPENAI_API_KEY}"
openai_base_url = "${OPENAI_BASE_URL}"
ollama_base_url = "${OLLAMA_BASE_URL}"
model_name = "${MODEL_NAME}"
model_embedding_name = "${MODEL_EMBEDDING_NAME}"
proxy_server = "${PROXY_SERVER}"
proxy_username = "${PROXY_USERNAME}"
proxy_password = "${PROXY_PASSWORD}"
searxng_endpoint = "${SEARXNG_ENDPOINT}"
searxng_engines = "${SEARXNG_ENGINES}"
searxng_categories = "${SEARXNG_CATEGORIES}"
supabase_anon_token = "${SUPABASE_ANON_TOKEN}"
supabase_url = "${SUPABASE_URL}"
supabase_service_token = "${SUPABASE_SERVICE_TOKEN}"
test_api_key = "${TEST_API_KEY}"
bull_auth_key = "${password:32}"
llamaparse_api_key = "${LLAMAPARSE_API_KEY}"
slack_webhook_url = "${SLACK_WEBHOOK_URL}"
posthog_api_key = "${POSTHOG_API_KEY}"
posthog_host = "${POSTHOG_HOST}"
max_cpu = "${MAX_CPU}"
max_ram = "${MAX_RAM}"
app_postgres_password = "${hash:32}"
nuq_postgres_password = "${hash:32}"
redis_password = "${hash:32}"
rabbitmq_password = "${hash:32}"
bull_auth_key = "${hash:32}"
firecrawl_org_id = "${uuid}"
firecrawl_team_id = "${uuid}"
# Dokploy's real hash helper returns exactly N hex characters. Force the UUID
# version and variant nibbles so Firecrawl's strict API-key parser accepts it.
firecrawl_api_key_a = "${hash:12}"
firecrawl_api_key_b = "${hash:3}"
firecrawl_api_key_c = "${hash:15}"
firecrawl_api_key = "fc-${firecrawl_api_key_a}4${firecrawl_api_key_b}8${firecrawl_api_key_c}"
self_hosted_webhook_hmac_secret = "${hash:32}"
[config]
env = [
"PORT=3002",
"HOST=0.0.0.0",
"USE_DB_AUTHENTICATION=false",
"APP_POSTGRES_PASSWORD=${app_postgres_password}",
"NUQ_POSTGRES_PASSWORD=${nuq_postgres_password}",
"REDIS_PASSWORD=${redis_password}",
"RABBITMQ_USER=firecrawl",
"RABBITMQ_PASSWORD=${rabbitmq_password}",
"BULL_AUTH_KEY=${bull_auth_key}",
"PLAYWRIGHT_MICROSERVICE_URL=http://playwright-service:3000/scrape",
"REDIS_URL=redis://redis:6379",
"REDIS_RATE_LIMIT_URL=redis://redis:6379",
"OPENAI_API_KEY=${openai_api_key}",
"OPENAI_BASE_URL=${openai_base_url}",
"OLLAMA_BASE_URL=${ollama_base_url}",
"MODEL_NAME=${model_name}",
"MODEL_EMBEDDING_NAME=${model_embedding_name}",
"PROXY_SERVER=${proxy_server}",
"PROXY_USERNAME=${proxy_username}",
"PROXY_PASSWORD=${proxy_password}",
"SEARXNG_ENDPOINT=${searxng_endpoint}",
"SEARXNG_ENGINES=${searxng_engines}",
"SEARXNG_CATEGORIES=${searxng_categories}",
"SUPABASE_ANON_TOKEN=${supabase_anon_token}",
"SUPABASE_URL=${supabase_url}",
"SUPABASE_SERVICE_TOKEN=${supabase_service_token}",
"TEST_API_KEY=${test_api_key}",
"LLAMAPARSE_API_KEY=${llamaparse_api_key}",
"SLACK_WEBHOOK_URL=${slack_webhook_url}",
"POSTHOG_API_KEY=${posthog_api_key}",
"POSTHOG_HOST=${posthog_host}",
"MAX_CPU=0.8",
"MAX_RAM=0.8"
"FIRECRAWL_ORG_ID=${firecrawl_org_id}",
"FIRECRAWL_TEAM_ID=${firecrawl_team_id}",
"FIRECRAWL_API_KEY=${firecrawl_api_key}",
"NUM_WORKERS_PER_QUEUE=8",
"CRAWL_CONCURRENT_REQUESTS=10",
"MAX_CONCURRENT_JOBS=5",
"BROWSER_POOL_SIZE=5",
"LOGGING_LEVEL=INFO",
"DISABLE_ENGPICKER=true",
"ALLOW_LOCAL_WEBHOOKS=false",
"BLOCK_MEDIA=true",
"OPENAI_API_KEY=",
"OPENAI_BASE_URL=",
"OPENROUTER_API_KEY=",
"OLLAMA_BASE_URL=",
"MODEL_NAME=",
"MODEL_EMBEDDING_NAME=",
"SEARCHAPI_API_KEY=",
"SEARXNG_ENDPOINT=",
"SEARXNG_ENGINES=",
"SEARXNG_CATEGORIES=",
"LLAMAPARSE_API_KEY=",
"SLACK_WEBHOOK_URL=",
"PROXY_SERVER=",
"PROXY_USERNAME=",
"PROXY_PASSWORD=",
"SELF_HOSTED_WEBHOOK_URL=",
"SELF_HOSTED_WEBHOOK_HMAC_SECRET=${self_hosted_webhook_hmac_secret}"
]
mounts = []
[[config.domains]]
serviceName = "api"
port = 3002
serviceName = "gateway"
port = 8080
host = "${main_domain}"
path = "/"
+19
View File
@@ -0,0 +1,19 @@
version: "3.8"
services:
kotonoba:
image: ghcr.io/trixtylab/kotonoba:1.0.19
restart: unless-stopped
environment:
NODE_ENV: production
DB_PATH: ${DB_PATH}
UPLOAD_DIR: ${UPLOAD_DIR}
JWT_SECRET: ${JWT_SECRET}
SITE_URL: ${SITE_URL}
ADMIN_DOMAIN: ${ADMIN_DOMAIN}
volumes:
- blog_data:/app/data
volumes:
blog_data: {}
+48
View File
@@ -0,0 +1,48 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="100%" height="100%">
<defs>
<!-- Primary Accent Gradient (Electric Indigo to Violet) -->
<linearGradient id="iconAccent" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#818CF8" />
<stop offset="100%" stop-color="#4F46E5" />
</linearGradient>
<!-- Cyan Highlight Gradient -->
<linearGradient id="iconCyan" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#38BDF8" />
<stop offset="100%" stop-color="#0284C7" />
</linearGradient>
</defs>
<!-- Pure Standalone Vector Symbol without Background Container -->
<g transform="translate(14, 10)">
<!-- Top Spark Dot (Kanji 言 Radical Spark) -->
<circle cx="80" cy="36" r="10" fill="url(#iconCyan)" />
<!-- Vertical Spine Pillar (CMS Core Column) -->
<rect x="68" y="58" width="24" height="144" rx="12" fill="url(#iconAccent)" />
<!-- Upper Speech Bar (Horizontal Japanese Radical Stroke) -->
<rect x="110" y="68" width="44" height="12" rx="6" fill="url(#iconCyan)" opacity="0.95" />
<!-- Geometric Dynamic Chevrons forming 'K' -->
<path
d="M 110 120
C 110 112, 116 105, 124 101
L 168 76
C 178 70, 191 78, 191 89
C 191 95, 187 101, 182 104
L 142 126
C 135 130, 135 136, 142 140
L 183 162
C 188 165, 191 171, 191 177
C 191 188, 178 196, 168 190
L 124 165
C 116 161, 110 154, 110 146
Z"
fill="url(#iconAccent)"
/>
<!-- Central Dialogue Focal Point Node -->
<circle cx="130" cy="133" r="9" fill="url(#iconCyan)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+18
View File
@@ -0,0 +1,18 @@
{
"id": "kotonoba",
"name": "Kotonoba",
"version": "1.0.19",
"description": "A high-performance, self-hosted, multi-tenant blog CMS built with Next.js 16, SQLite, Tailwind CSS v4, and Docker.",
"logo": "icon.svg",
"links": {
"github": "https://github.com/trixtylab/kotonoba",
"website": "https://github.com/trixtylab/kotonoba",
"docs": "https://github.com/TrixtyLab/kotonoba/tree/master/docs"
},
"tags": [
"blog",
"i18n",
"nextjs",
"web"
]
}
+20
View File
@@ -0,0 +1,20 @@
[variables]
main_domain = "${domain}"
jwt_secret = "${password:32}"
[config]
env = [
"NODE_ENV=production",
"DB_PATH=/app/data/kotonoba.db",
"UPLOAD_DIR=/app/data/uploads",
"JWT_SECRET=${jwt_secret}",
"SITE_URL=https://${main_domain}",
"ADMIN_DOMAIN=${main_domain}"
]
mounts = []
[[config.domains]]
serviceName = "kotonoba"
port = 3000
host = "${main_domain}"
+9 -1
View File
@@ -3,9 +3,17 @@ main_domain = "${domain}"
[config]
env = {}
mounts = []
[[config.domains]]
serviceName = "web"
port = 8_069
host = "${main_domain}"
[[config.mounts]]
filePath = "config/odoo.conf"
content = """
[options]
addons_path = /mnt/extra-addons
data_dir = /var/lib/odoo
proxy_mode = True
"""
+9 -1
View File
@@ -3,9 +3,17 @@ main_domain = "${domain}"
[config]
env = {}
mounts = []
[[config.domains]]
serviceName = "web"
port = 8_069
host = "${main_domain}"
[[config.mounts]]
filePath = "config/odoo.conf"
content = """
[options]
addons_path = /mnt/extra-addons
data_dir = /var/lib/odoo
proxy_mode = True
"""
+9 -1
View File
@@ -3,9 +3,17 @@ main_domain = "${domain}"
[config]
env = {}
mounts = []
[[config.domains]]
serviceName = "web"
port = 8_069
host = "${main_domain}"
[[config.mounts]]
filePath = "config/odoo.conf"
content = """
[options]
addons_path = /mnt/extra-addons
data_dir = /var/lib/odoo
proxy_mode = True
"""
+197
View File
@@ -0,0 +1,197 @@
services:
libsql:
image: ghcr.io/tursodatabase/libsql-server:v0.24.33
restart: unless-stopped
expose:
- 8080
volumes:
- libsql-data:/var/lib/sqld
environment:
- SQLD_NODE=primary
# Increase limits to handle multiple services without connection pooling
- SQLD_MAX_CONCURRENT_CONNECTIONS=512
- SQLD_MAX_CONCURRENT_REQUESTS=1024
healthcheck:
test:
[
"CMD-SHELL",
'perl -e ''use IO::Socket::INET; exit(IO::Socket::INET->new(PeerAddr=>"127.0.0.1:8080",Timeout=>1) ? 0 : 1);''',
]
interval: 10s
timeout: 5s
retries: 5
start_period: 10s
# One-shot migration runner (idempotent — drizzle skips applied migrations).
# No published image exists for it upstream, so it is built from the pinned
# commit the ghcr.io app images were built from.
db-migrate:
build:
context: https://github.com/openstatusHQ/openstatus.git#e1a98255e39c9cc9f6d4853dbce30c3f6c42514a
dockerfile: packages/db/Dockerfile
restart: on-failure:3
environment:
- DATABASE_URL=http://libsql:8080
- DATABASE_AUTH_TOKEN=
depends_on:
libsql:
condition: service_healthy
workflows:
image: ghcr.io/openstatushq/openstatus-workflows:e1a9825
restart: unless-stopped
expose:
- 3000
volumes:
- workflows-data:/app/data
environment:
- NODE_ENV=production
- PORT=3000
- DATABASE_URL=http://libsql:8080
- DATABASE_AUTH_TOKEN=
- CRON_SECRET=${CRON_SECRET}
- RESEND_API_KEY=${RESEND_API_KEY:-re_selfhost_placeholder}
- SITE_URL=https://${DASHBOARD_HOST}
- TINYBIRD_NOOP=true
depends_on:
db-migrate:
condition: service_completed_successfully
libsql:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/ping"]
interval: 15s
timeout: 10s
retries: 3
start_period: 30s
server:
image: ghcr.io/openstatushq/openstatus-server:e1a9825
restart: unless-stopped
expose:
- 3000
environment:
- NODE_ENV=production
- PORT=3000
- DATABASE_URL=http://libsql:8080
- DATABASE_AUTH_TOKEN=
- CRON_SECRET=${CRON_SECRET}
- RESEND_API_KEY=${RESEND_API_KEY:-re_selfhost_placeholder}
- SUPER_ADMIN_TOKEN=${SUPER_ADMIN_TOKEN}
- SELF_HOST=true
- FLY_REGION=self-hosted
- TINYBIRD_NOOP=true
- UPSTASH_REDIS_REST_URL=http://localhost:6379
- UPSTASH_REDIS_REST_TOKEN=placeholder
depends_on:
db-migrate:
condition: service_completed_successfully
workflows:
condition: service_healthy
libsql:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/ping"]
interval: 15s
timeout: 10s
retries: 3
start_period: 30s
dashboard:
image: ghcr.io/openstatushq/openstatus-dashboard:e1a9825
restart: unless-stopped
expose:
- 3000
environment:
- NODE_ENV=production
- PORT=3000
- HOSTNAME=0.0.0.0
- AUTH_TRUST_HOST=true
- DATABASE_URL=http://libsql:8080
- DATABASE_AUTH_TOKEN=
- AUTH_SECRET=${AUTH_SECRET}
- SELF_HOST=true
- NEXT_PUBLIC_URL=https://${DASHBOARD_HOST}
- CRON_SECRET=${CRON_SECRET}
- RESEND_API_KEY=${RESEND_API_KEY:-re_selfhost_placeholder}
- TINYBIRD_NOOP=true
- UPSTASH_REDIS_REST_URL=http://localhost:6379
- UPSTASH_REDIS_REST_TOKEN=placeholder
depends_on:
db-migrate:
condition: service_completed_successfully
workflows:
condition: service_healthy
libsql:
condition: service_healthy
server:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/"]
interval: 15s
timeout: 10s
retries: 3
start_period: 45s
status-page:
image: ghcr.io/openstatushq/openstatus-status-page:e1a9825
restart: unless-stopped
expose:
- 3000
environment:
- NODE_ENV=production
- PORT=3000
- HOSTNAME=0.0.0.0
- AUTH_TRUST_HOST=true
- DATABASE_URL=http://libsql:8080
- DATABASE_AUTH_TOKEN=
- AUTH_SECRET=${AUTH_SECRET}
- SELF_HOST=true
- NEXT_PUBLIC_URL=https://${DASHBOARD_HOST}
- CRON_SECRET=${CRON_SECRET}
- RESEND_API_KEY=${RESEND_API_KEY:-re_selfhost_placeholder}
- TINYBIRD_NOOP=true
depends_on:
db-migrate:
condition: service_completed_successfully
workflows:
condition: service_healthy
libsql:
condition: service_healthy
server:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/"]
interval: 15s
timeout: 10s
retries: 3
start_period: 45s
# Ingest server for private-location probes (the probe itself,
# ghcr.io/openstatushq/private-location, runs wherever you monitor from).
private-location:
image: ghcr.io/openstatushq/openstatus-private-location:e1a9825
restart: unless-stopped
expose:
- 8080
environment:
- PORT=8080
- GIN_MODE=release
- DB_URL=http://libsql:8080
- WORKFLOWS_URL=http://workflows:3000
- CRON_SECRET=${CRON_SECRET}
- TINYBIRD_URL=${TINYBIRD_URL}
- TINYBIRD_TOKEN=${TINYBIRD_TOKEN}
depends_on:
server:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/health"]
interval: 15s
timeout: 10s
retries: 3
start_period: 30s
volumes:
libsql-data:
workflows-data:
+60
View File
@@ -0,0 +1,60 @@
# OpenStatus
OpenStatus is deployed here in self-hosted mode following the official Docker
setup (`DOCKER.md` / `docker-compose.github-packages.yaml` upstream), with all
app images pinned to the same upstream commit (`e1a9825`).
## Services
| Service | Purpose |
|---|---|
| dashboard | Admin UI (your first domain) |
| status-page | Public status pages (your second domain, pages served at `/<slug>`) |
| server | API backend (tRPC / public API) |
| workflows | Background jobs / alerting |
| libsql | Local libSQL (Turso) database — no cloud account needed |
| db-migrate | One-shot migration runner (built from the pinned upstream commit, exits after applying) |
| private-location | Ingest server that receives check results from your monitoring probes |
Database migrations run automatically on every deploy (the one-shot
`db-migrate` service is idempotent).
Note: `db-migrate` is built from source (upstream publishes no image for it),
so the first deployment takes a few extra minutes while it builds.
## First login
1. Open the dashboard domain and choose **magic link** login with your e-mail.
2. If you did not configure `RESEND_API_KEY`, the magic link is not e-mailed —
it is printed to the **dashboard service logs**. Open the dashboard
container logs in Dokploy and copy the `>>> Magic Link: ...` URL into your
browser. If the printed link starts with `http://0.0.0.0:3000`, replace that
host with your dashboard domain before opening it. (Optionally, once HTTPS
is enabled on your dashboard domain, you can add
`AUTH_URL=https://<your-dashboard-domain>` to the dashboard service in the
compose file so links are generated with the right host — do not set it to
an empty value or a plain-HTTP domain, as that breaks the auth flow.)
3. Create a workspace and a status page with a slug; it becomes available at
`https://<status-page-domain>/<slug>`.
## Running checks (probes)
Self-hosted OpenStatus runs monitors through **private location probes**
(`ghcr.io/openstatushq/private-location`) that you deploy wherever you want to
monitor from. Point the probe at this stack's ingest server:
- Expose the `private-location` service (port 8080) on a domain in Dokploy, and
- set `OPENSTATUS_INGEST_URL` on the probe to that URL, together with the
`OPENSTATUS_KEY` you create in the dashboard.
See the upstream guide: https://www.openstatus.dev/docs (Guides → How to
Self-Host openstatus).
## Analytics (optional)
Tinybird analytics are disabled by default (`TINYBIRD_NOOP=true`), so charts
show no data but everything else works. To enable analytics, run a Tinybird
Local container or use Tinybird Cloud, deploy the `packages/tinybird` project
with the `tb` CLI, and set `TINYBIRD_URL` / `TINYBIRD_TOKEN` in the
environment (plus `TINY_BIRD_API_KEY` and remove `TINYBIRD_NOOP`) — see the
upstream self-hosting guide.
+17
View File
@@ -0,0 +1,17 @@
{
"id": "openstatus",
"name": "OpenStatus",
"version": "e1a9825",
"description": "OpenStatus is an open-source synthetic monitoring platform with beautiful status pages, uptime and API monitoring as code, and alerting.",
"logo": "openstatus.png",
"links": {
"github": "https://github.com/openstatusHQ/openstatus",
"website": "https://www.openstatus.dev",
"docs": "https://www.openstatus.dev/docs"
},
"tags": [
"monitoring",
"status-page",
"uptime"
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

+34
View File
@@ -0,0 +1,34 @@
[variables]
dashboard_domain = "${domain}"
status_page_domain = "${domain}"
auth_secret = "${base64:32}"
cron_secret = "${password:32}"
super_admin_token = "${password:32}"
[config]
# Dashboard (admin UI — log in here)
[[config.domains]]
serviceName = "dashboard"
port = 3000
host = "${dashboard_domain}"
# Public status pages (served at /<page-slug>)
[[config.domains]]
serviceName = "status-page"
port = 3000
host = "${status_page_domain}"
[config.env]
DASHBOARD_HOST = "${dashboard_domain}"
STATUS_PAGE_HOST = "${status_page_domain}"
AUTH_SECRET = "${auth_secret}"
CRON_SECRET = "${cron_secret}"
SUPER_ADMIN_TOKEN = "${super_admin_token}"
# Optional: Resend API key for outgoing e-mails. Without it, magic-link
# login URLs are printed to the dashboard container logs instead.
RESEND_API_KEY = ""
# Optional: Tinybird analytics for the private-location ingest server
# (see instructions — analytics are disabled/no-op by default)
TINYBIRD_URL = ""
TINYBIRD_TOKEN = ""
+230
View File
@@ -0,0 +1,230 @@
services:
postgres:
image: postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15
environment:
POSTGRES_USER: orbit
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?required}
POSTGRES_DB: orbit
volumes:
- postgres:/var/lib/postgresql
healthcheck:
test:
- CMD-SHELL
- pg_isready -U orbit -d orbit
interval: 5s
timeout: 3s
retries: 20
restart: unless-stopped
redis:
image: redis:8-alpine@sha256:978f0e01593e65eed801f2402944efcd936d43b5027e4908a7897baf88ed6241
command:
- redis-server
- --appendonly
- 'yes'
volumes:
- redis:/data
healthcheck:
test:
- CMD
- redis-cli
- ping
interval: 5s
timeout: 3s
retries: 20
restart: unless-stopped
storage:
# SeaweedFS S3 gateway (MinIO images are no longer publicly pullable).
image: chrislusf/seaweedfs:4.48
entrypoint:
- /bin/sh
- -c
command:
- |
set -e
printf '{"identities":[{"name":"orbit","credentials":[{"accessKey":"orbit","secretKey":"%s"}],"actions":["Admin","Read","Write","List","Tagging"]}]}' "$$MINIO_PASSWORD" > /tmp/s3.json
exec weed server -dir=/data -ip.bind=0.0.0.0 -s3 -s3.port=9000 -s3.config=/tmp/s3.json
environment:
MINIO_PASSWORD: ${MINIO_PASSWORD:?required}
volumes:
- storage:/data
healthcheck:
test:
- CMD-SHELL
- wget -q -O /dev/null http://127.0.0.1:9000/healthz || exit 1
interval: 5s
timeout: 3s
retries: 30
restart: unless-stopped
expose:
- '9000'
bucket:
# Reuses the mc client from orbit-bucket to create the bucket and its CORS rule on SeaweedFS.
image: ghcr.io/noveum/orbit-bucket:2026.09.24
entrypoint:
- /bin/sh
- -c
command:
- |
set -eu
until mc alias set orbit http://storage:9000 orbit "$$MINIO_PASSWORD" --api S3v4; do sleep 2; done
until mc mb --ignore-existing orbit/orbit-uploads; do sleep 2; done
printf '<CORSConfiguration><CORSRule><AllowedOrigin>%s</AllowedOrigin><AllowedMethod>GET</AllowedMethod><AllowedMethod>PUT</AllowedMethod><AllowedMethod>HEAD</AllowedMethod><AllowedHeader>*</AllowedHeader><ExposeHeader>ETag</ExposeHeader><MaxAgeSeconds>3600</MaxAgeSeconds></CORSRule></CORSConfiguration>' "$$ORBIT_APP_URL" > /tmp/cors.xml
until mc cors set orbit/orbit-uploads /tmp/cors.xml; do sleep 2; done
touch /tmp/initialized
exec tail -f /dev/null
environment:
MINIO_PASSWORD: ${MINIO_PASSWORD:?required}
ORBIT_APP_URL: ${ORBIT_APP_URL:?required}
depends_on:
storage:
condition: service_healthy
restart: unless-stopped
healthcheck:
test:
- CMD-SHELL
- test -f /tmp/initialized
interval: 5s
timeout: 3s
retries: 30
web:
image: ghcr.io/noveum/orbit-runtime:2026.09.24
environment:
DATABASE_URL: postgres://orbit:${POSTGRES_PASSWORD:?required}@postgres:5432/orbit
DIRECT_URL: postgres://orbit:${POSTGRES_PASSWORD:?required}@postgres:5432/orbit
DATABASE_PREPARED_STATEMENTS: 'true'
REDIS_URL: redis://redis:6379
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:?required}
BETTER_AUTH_URL: ${ORBIT_APP_URL:?required}
NEXT_PUBLIC_APP_URL: ${ORBIT_APP_URL:?required}
ORBIT_PASSWORD_AUTH: ${ORBIT_PASSWORD_AUTH:-true}
ALLOWED_EMAIL_DOMAINS: ${ALLOWED_EMAIL_DOMAINS:-}
RESEND_API_KEY: ${RESEND_API_KEY:-}
EMAIL_FROM: ${EMAIL_FROM:-Orbit <auth@orbit.local>}
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-}
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-}
GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID:-}
GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET:-}
GITHUB_APP_SLUG: ${GITHUB_APP_SLUG:-}
GITHUB_APP_ID: ${GITHUB_APP_ID:-}
GITHUB_APP_PRIVATE_KEY: ${GITHUB_APP_PRIVATE_KEY:-}
GITHUB_APP_CLIENT_ID: ${GITHUB_APP_CLIENT_ID:-}
GITHUB_APP_CLIENT_SECRET: ${GITHUB_APP_CLIENT_SECRET:-}
GITHUB_WEBHOOK_SECRET: ${GITHUB_WEBHOOK_SECRET:-}
SLACK_ENABLED: ${SLACK_ENABLED:-false}
SLACK_CLIENT_ID: ${SLACK_CLIENT_ID:-}
SLACK_CLIENT_SECRET: ${SLACK_CLIENT_SECRET:-}
SLACK_SIGNING_SECRET: ${SLACK_SIGNING_SECRET:-}
SLACK_APP_ID: ${SLACK_APP_ID:-}
CRON_SECRET: ${CRON_SECRET:-}
ORBIT_SELF_HOSTED: 'true'
S3_ENDPOINT: ${ORBIT_STORAGE_URL:?required}
S3_REGION: us-east-1
S3_BUCKET: orbit-uploads
S3_ACCESS_KEY_ID: orbit
S3_SECRET_ACCESS_KEY: ${MINIO_PASSWORD:?required}
NEXT_TELEMETRY_DISABLED: '1'
healthcheck:
test:
- CMD
- node
- -e
- fetch('http://127.0.0.1:3000/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))
interval: 10s
timeout: 5s
start_period: 30s
retries: 5
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
bucket:
condition: service_healthy
restart: unless-stopped
realtime:
image: ghcr.io/noveum/orbit-runtime:2026.09.24
command:
- node
- apps/web/realtime.mjs
environment:
DATABASE_URL: postgres://orbit:${POSTGRES_PASSWORD:?required}@postgres:5432/orbit
DIRECT_URL: postgres://orbit:${POSTGRES_PASSWORD:?required}@postgres:5432/orbit
DATABASE_PREPARED_STATEMENTS: 'true'
REDIS_URL: redis://redis:6379
BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET:?required}
BETTER_AUTH_URL: ${ORBIT_APP_URL:?required}
NEXT_PUBLIC_APP_URL: ${ORBIT_APP_URL:?required}
ORBIT_PASSWORD_AUTH: ${ORBIT_PASSWORD_AUTH:-true}
ALLOWED_EMAIL_DOMAINS: ${ALLOWED_EMAIL_DOMAINS:-}
RESEND_API_KEY: ${RESEND_API_KEY:-}
EMAIL_FROM: ${EMAIL_FROM:-Orbit <auth@orbit.local>}
GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID:-}
GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET:-}
GITHUB_CLIENT_ID: ${GITHUB_CLIENT_ID:-}
GITHUB_CLIENT_SECRET: ${GITHUB_CLIENT_SECRET:-}
GITHUB_APP_SLUG: ${GITHUB_APP_SLUG:-}
GITHUB_APP_ID: ${GITHUB_APP_ID:-}
GITHUB_APP_PRIVATE_KEY: ${GITHUB_APP_PRIVATE_KEY:-}
GITHUB_APP_CLIENT_ID: ${GITHUB_APP_CLIENT_ID:-}
GITHUB_APP_CLIENT_SECRET: ${GITHUB_APP_CLIENT_SECRET:-}
GITHUB_WEBHOOK_SECRET: ${GITHUB_WEBHOOK_SECRET:-}
SLACK_ENABLED: ${SLACK_ENABLED:-false}
SLACK_CLIENT_ID: ${SLACK_CLIENT_ID:-}
SLACK_CLIENT_SECRET: ${SLACK_CLIENT_SECRET:-}
SLACK_SIGNING_SECRET: ${SLACK_SIGNING_SECRET:-}
SLACK_APP_ID: ${SLACK_APP_ID:-}
CRON_SECRET: ${CRON_SECRET:-}
ORBIT_SELF_HOSTED: 'true'
S3_ENDPOINT: ${ORBIT_STORAGE_URL:?required}
S3_REGION: us-east-1
S3_BUCKET: orbit-uploads
S3_ACCESS_KEY_ID: orbit
S3_SECRET_ACCESS_KEY: ${MINIO_PASSWORD:?required}
NEXT_TELEMETRY_DISABLED: '1'
healthcheck:
test:
- CMD
- node
- -e
- fetch('http://127.0.0.1:3100/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))
interval: 10s
timeout: 5s
retries: 5
depends_on:
web:
condition: service_healthy
bucket:
condition: service_healthy
restart: unless-stopped
scheduler:
image: ghcr.io/noveum/orbit-runtime:2026.09.24
command:
- node
- apps/web/scheduler.mjs
environment:
CRON_SECRET: ${CRON_SECRET:?required}
ORBIT_INTERNAL_URL: http://web:3000
stop_grace_period: 310s
depends_on:
web:
condition: service_healthy
restart: unless-stopped
orbit:
image: ghcr.io/noveum/orbit-gateway:2026.09.24
environment:
ORBIT_TRUSTED_PROXIES: ${ORBIT_TRUSTED_PROXIES:-private_ranges}
depends_on:
web:
condition: service_healthy
realtime:
condition: service_healthy
scheduler:
condition: service_started
restart: unless-stopped
expose:
- '3000'
volumes:
postgres: null
redis: null
storage: null
version: '3.8'
+13
View File
@@ -0,0 +1,13 @@
# Orbit by Noveum
Deploy, then enable HTTPS with a valid certificate for both generated domains
in Dokploy's Domains tab. Open the Orbit HTTPS domain and create your account
and workspace. Both the app and storage domains require working HTTPS. Database migrations,
bucket creation and the storage CORS allowlist are configured automatically.
PostgreSQL, Redis and SeaweedFS (S3 storage) use persistent volumes. Preserve generated secrets
and back up PostgreSQL and the SeaweedFS storage volume before upgrades. Run one scheduler instance.
Configure Resend and EMAIL_FROM on web to send invitations and password recovery.
Configure ALLOWED_EMAIL_DOMAINS to restrict signup to your team.
Hosted MCP: https://orbit.noveum.ai/mcp
Self-hosted MCP: your Orbit HTTPS domain followed by /mcp.
+18
View File
@@ -0,0 +1,18 @@
{
"id": "orbit",
"name": "Orbit by Noveum",
"version": "2026.09.24",
"description": "Open-source task management with issues, projects, sprints, docs, files, realtime collaboration and an OAuth MCP endpoint.",
"logo": "orbit.png",
"links": {
"github": "https://github.com/Noveum/orbit",
"website": "https://orbit.noveum.ai",
"docs": "https://github.com/Noveum/orbit/blob/containers-2026.09.24/docs/container-catalogs.md"
},
"tags": [
"productivity",
"project-management",
"collaboration",
"mcp"
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

+27
View File
@@ -0,0 +1,27 @@
[variables]
main_domain = "${domain}"
storage_domain = "${domain}"
postgres_password = "${password:48}"
minio_password = "${password:48}"
auth_secret = "${password:64}"
cron_secret = "${password:64}"
[config]
env = [
"ORBIT_APP_URL=https://${main_domain}",
"ORBIT_STORAGE_URL=https://${storage_domain}",
"POSTGRES_PASSWORD=${postgres_password}",
"MINIO_PASSWORD=${minio_password}",
"BETTER_AUTH_SECRET=${auth_secret}",
"CRON_SECRET=${cron_secret}",
]
[[config.domains]]
serviceName = "orbit"
port = 3000
host = "${main_domain}"
[[config.domains]]
serviceName = "storage"
port = 9000
host = "${storage_domain}"
+13 -2
View File
@@ -9,7 +9,7 @@ services:
- POSTGRES_PASSWORD=postgres
plausible_events_db:
image: clickhouse/clickhouse-server:24.3.3.102-alpine
image: clickhouse/clickhouse-server:24.12-alpine
restart: always
volumes:
@@ -21,20 +21,31 @@ services:
nofile:
soft: 262144
hard: 262144
environment:
# Required since ClickHouse 24.11+ images: skip the default-user setup so
# Plausible can connect as "default" without a password (matches the
# official CE v3 compose.yml).
- CLICKHOUSE_SKIP_USER_SETUP=1
plausible:
image: ghcr.io/plausible/community-edition:v2.1.5
image: ghcr.io/plausible/community-edition:v3.2.1
restart: always
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
depends_on:
- plausible_db
- plausible_events_db
volumes:
- plausible-data:/var/lib/plausible
environment:
- TMPDIR=/var/lib/plausible/tmp
env_file:
- .env
volumes:
db-data:
driver: local
plausible-data:
driver: local
event-data:
driver: local
event-logs:
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "plausible",
"name": "Plausible",
"version": "v2.1.5",
"version": "v3.2.1",
"description": "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.",
"logo": "logo.svg",
"links": {
+9 -5
View File
@@ -1,15 +1,19 @@
version: "3.8"
services:
pocket-id:
image: ghcr.io/pocket-id/pocket-id:v1
image: pocketid/pocket-id:v2.14.0
restart: unless-stopped
environment:
- APP_URL
- TRUST_PROXY
- ENCRYPTION_KEY
APP_URL: ${APP_URL}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
TRUST_PROXY: ${TRUST_PROXY}
volumes:
- pocket-id-data:/app/data
expose:
- "1411"
healthcheck:
test: [ "CMD", "/app/pocket-id", "healthcheck" ]
test: ["CMD", "/app/pocket-id", "healthcheck"]
interval: 1m30s
timeout: 5s
retries: 2
+10 -4
View File
@@ -1,16 +1,22 @@
{
"id": "pocket-id",
"name": "Pocket ID",
"version": "v1",
"description": "A simple and easy-to-use OIDC provider that allows users to authenticate with their passkeys to your services.",
"version": "2.14.0",
"description": "Pocket ID is a simple OpenID Connect and OAuth 2.0 provider that lets users authenticate to applications with passkeys.",
"logo": "pocket-id.svg",
"links": {
"github": "https://github.com/pocket-id/pocket-id",
"website": "https://pocket-id.org/",
"docs": "https://pocket-id.org/docs"
"docs": "https://pocket-id.org/docs/"
},
"tags": [
"authentication",
"identity",
"auth"
"sso",
"oidc",
"oauth2",
"passkeys",
"security",
"self-hosted"
]
}
+3 -2
View File
@@ -1,5 +1,6 @@
[variables]
main_domain = "${domain}"
encryption_key = "${base64:32}"
[config]
mounts = []
@@ -10,6 +11,6 @@ port = 1411
host = "${main_domain}"
[config.env]
ENCRYPTION_KEY = "CHANGEME: openssl rand -base64 32"
APP_URL = "http://${main_domain}"
APP_URL = "https://${main_domain}"
ENCRYPTION_KEY = "${encryption_key}"
TRUST_PROXY = "true"
+23 -5
View File
@@ -12,12 +12,15 @@ services:
JWT_SECRET: ${JWT_SECRET}
DATABASE_URL: "postgresql://${DB_USER}:${DB_PASSWORD}@postiz-postgres:5432/${DB_NAME}"
REDIS_URL: "redis://postiz-redis:6379"
BACKEND_INTERNAL_URL: "http://localhost:3000"
# "localhost" resolves to ::1 first, but the backend only listens on IPv4.
BACKEND_INTERNAL_URL: "http://127.0.0.1:3000"
TEMPORAL_ADDRESS: "temporal:7233"
IS_GENERAL: "true"
DISABLE_REGISTRATION: "false"
DISABLE_REGISTRATION: ${DISABLE_REGISTRATION:-false}
RUN_CRON: "true"
NOT_SECURED: "true"
# Postiz only checks whether NOT_SECURED is set (any value, even "false",
# enables insecure mode). The template is served over HTTPS, so keep it
# unset to get secure cookies.
STORAGE_PROVIDER: "local"
UPLOAD_DIRECTORY: "/uploads"
NEXT_PUBLIC_UPLOAD_DIRECTORY: "/uploads"
@@ -25,12 +28,22 @@ services:
# === Social Media API Settings ===
# These variables should be defined in the Dokploy Environment tab.
# For more configuration options and details, please refer to the official documentation.
# Every provider Postiz reads must be listed here, otherwise setting it in
# the Environment tab has no effect: it never reaches the container.
FACEBOOK_APP_ID: ${FACEBOOK_APP_ID}
FACEBOOK_APP_SECRET: ${FACEBOOK_APP_SECRET}
INSTAGRAM_APP_ID: ${INSTAGRAM_APP_ID}
INSTAGRAM_APP_SECRET: ${INSTAGRAM_APP_SECRET}
LINKEDIN_CLIENT_ID: ${LINKEDIN_CLIENT_ID}
LINKEDIN_CLIENT_SECRET: ${LINKEDIN_CLIENT_SECRET}
X_API_KEY: ${X_API_KEY}
X_API_SECRET: ${X_API_SECRET}
# YOUTUBE_CLIENT_ID/SECRET also power "Sign in with Google" (Postiz's
# Google auth provider reads YOUTUBE_CLIENT_ID).
YOUTUBE_CLIENT_ID: ${YOUTUBE_CLIENT_ID}
YOUTUBE_CLIENT_SECRET: ${YOUTUBE_CLIENT_SECRET}
TIKTOK_CLIENT_ID: ${TIKTOK_CLIENT_ID}
TIKTOK_CLIENT_SECRET: ${TIKTOK_CLIENT_SECRET}
volumes:
- postiz-config:/config/
@@ -41,7 +54,8 @@ services:
postiz-redis:
condition: service_healthy
temporal:
condition: service_started
# Postiz crashes on boot if Temporal is not accepting connections yet.
condition: service_healthy
postiz-postgres:
image: postgres:17-alpine
@@ -74,6 +88,7 @@ services:
temporal-elasticsearch:
image: elasticsearch:7.17.27
restart: always
environment:
- cluster.routing.allocation.disk.threshold_enabled=true
- cluster.routing.allocation.disk.watermark.low=512mb
@@ -87,6 +102,7 @@ services:
temporal-postgresql:
image: postgres:16
restart: always
environment:
POSTGRES_PASSWORD: ${TEMPORAL_PASSWORD}
POSTGRES_USER: ${TEMPORAL_USER}
@@ -100,6 +116,7 @@ services:
temporal:
image: temporalio/auto-setup:1.28.1
restart: always
depends_on:
temporal-postgresql:
condition: service_healthy
@@ -116,7 +133,8 @@ services:
- ES_VERSION=v7
- TEMPORAL_NAMESPACE=default
healthcheck:
test: ["CMD", "tctl", "--address", "127.0.0.1:7233", "workflow", "list"]
# tctl needs an explicit namespace, and Temporal does not bind 127.0.0.1.
test: ["CMD-SHELL", "tctl --address $$(hostname -i):7233 --namespace default workflow list > /dev/null 2>&1"]
interval: 15s
timeout: 10s
retries: 30
+7 -1
View File
@@ -18,10 +18,16 @@ env = [
"TEMPORAL_PASSWORD=${temporal_password}",
"FACEBOOK_APP_ID=",
"FACEBOOK_APP_SECRET=",
"INSTAGRAM_APP_ID=",
"INSTAGRAM_APP_SECRET=",
"LINKEDIN_CLIENT_ID=",
"LINKEDIN_CLIENT_SECRET=",
"X_API_KEY=",
"X_API_SECRET="
"X_API_SECRET=",
"YOUTUBE_CLIENT_ID=",
"YOUTUBE_CLIENT_SECRET=",
"TIKTOK_CLIENT_ID=",
"TIKTOK_CLIENT_SECRET="
]
mounts = []
+15
View File
@@ -0,0 +1,15 @@
version: "3.8"
services:
quireink:
image: quireink/quireink:2.2.1
restart: unless-stopped
environment:
SITE_URL: https://${QUIREINK_HOST}
volumes:
- quireink-data:/var/lib/quire/data
- quireink-uploads:/var/lib/quire/uploads
volumes:
quireink-data: {}
quireink-uploads: {}
+13
View File
@@ -0,0 +1,13 @@
{
"id": "quireink",
"name": "Quire Ink",
"version": "2.2.1",
"description": "A self-hosted blog in one container: a real editor over Markdown with tables, footnotes and mathematics, scheduled publishing, newsletters over your own SMTP, comments with a built-in spam gate, cookie-free analytics, full-text search and a book reading mode. Importers bring a WordPress, Ghost, Substack or Medium blog across with its redirects and images. There is no database service to run and nothing is fetched from a third party: two SQLite files and an uploads folder are the whole of it. After the first start, open the container logs for the one-time link that claims the blog and creates your account.",
"logo": "quireink.png",
"links": {
"github": "https://github.com/joiha-steven/quireink",
"website": "https://quireink.com",
"docs": "https://github.com/joiha-steven/quireink/blob/main/docs/self-host-docker.md"
},
"tags": ["cms", "self-hosted", "blog"]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+11
View File
@@ -0,0 +1,11 @@
[variables]
main_domain = "${domain}"
[config]
env = ["QUIREINK_HOST=${main_domain}"]
mounts = []
[[config.domains]]
serviceName = "quireink"
port = 3000
host = "${main_domain}"
@@ -0,0 +1,33 @@
version: "3.8"
services:
rustrak-server:
image: rustrak/rustrak-server:latest
restart: unless-stopped
environment:
- HOST=0.0.0.0
- PORT=8080
- RUST_LOG=info
- SSL_PROXY=true
- SESSION_SECRET_KEY=${SESSION_SECRET_KEY}
- CREATE_SUPERUSER=${CREATE_SUPERUSER}
volumes:
- rustrak_data:/data
expose:
- 8080
rustrak-ui:
image: rustrak/rustrak-ui:latest
restart: unless-stopped
environment:
- RUSTRAK_API_URL=${RUSTRAK_API_URL}
# Next.js standalone binds to $HOSTNAME (the container id) by default, which is
# unreachable from Traefik on the Dokploy network -> 502. Bind to all interfaces.
- HOSTNAME=0.0.0.0
expose:
- 3000
depends_on:
- rustrak-server
volumes:
rustrak_data:
+20
View File
@@ -0,0 +1,20 @@
{
"id": "rustrak-sqlite",
"name": "Rustrak (Full Stack SQLite)",
"version": "latest",
"description": "Self-hosted error tracking compatible with Sentry SDKs. Full stack with the Rust server, web dashboard and built-in SQLite storage. No external database required.",
"logo": "rustrak.svg",
"links": {
"github": "https://github.com/rustrak/rustrak",
"website": "https://rustrak.github.io/rustrak/",
"docs": "https://rustrak.github.io/rustrak/getting-started/overview"
},
"tags": [
"monitoring",
"error-tracking",
"sentry",
"rust",
"observability",
"sqlite"
]
}
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.8 KiB

+25
View File
@@ -0,0 +1,25 @@
[variables]
main_domain = "${domain}"
api_domain = "${domain}"
session_secret_key = "${password:64}"
admin_email = "${email}"
admin_password = "${password:16}"
create_superuser = "${admin_email}:${admin_password}"
rustrak_api_url = "https://${api_domain}"
[config]
env = [
"SESSION_SECRET_KEY=${session_secret_key}",
"CREATE_SUPERUSER=${create_superuser}",
"RUSTRAK_API_URL=${rustrak_api_url}",
]
[[config.domains]]
serviceName = "rustrak-ui"
port = 3000
host = "${main_domain}"
[[config.domains]]
serviceName = "rustrak-server"
port = 8080
host = "${api_domain}"
+16
View File
@@ -0,0 +1,16 @@
services:
silo:
image: pgsty/silo:RELEASE.2026-08-06T00-00-00Z
restart: unless-stopped
expose:
- 9000
- 9001
environment:
- MINIO_ROOT_USER=${MINIO_ROOT_USER}
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
volumes:
- silo:/data
command: server /data --console-address ":9001"
volumes:
silo:
+82
View File
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
width="570" height="570" viewBox="230 213 570 570"
preserveAspectRatio="xMidYMid meet"
role="img" aria-labelledby="title desc"
shape-rendering="geometricPrecision">
<title id="title">SILO emblem</title>
<desc id="desc">A smooth circular SILO mark with a peaked roof, flowing left wall, columns, and a curved base.</desc>
<defs>
<linearGradient id="silo-color" x1="276" y1="720" x2="742" y2="286" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#064A83"/>
<stop offset="0.5" stop-color="#007FA8"/>
<stop offset="1" stop-color="#22C7C9"/>
</linearGradient>
</defs>
<g fill="url(#silo-color)">
<!-- Outer circular band, intentionally opened at the lower-right plinth. -->
<path d="
M 734 676
A 283.5 278.5 0 1 0 310 692
L 359 692
A 247 248.5 0 1 1 688 676
Z"/>
<!-- Flowing left wall; its upper tangent matches the inner ellipse. -->
<path d="
M 300.57 375
C 292 390 300 430 328 450
C 343 461 357 472 374 481
C 410 501 426 517 426 544
L 426 676
L 336 676
C 308 647 286 608 274 565
C 262 522 263 479 272 439
C 278 413 286 389 300.57 375
Z"/>
<!-- Right column with tangent-continuous upper shoulder. -->
<path d="
M 602 373
Q 602 368 607 370
C 619 374 634 381 634 389
L 634 647
Q 634 649 636 649
L 708 649
L 734 676
L 602 676
Z"/>
<!-- Lower circular cap. -->
<path d="
M 310 692
L 714 692
C 668 743 596 774 512 774
C 428 774 355 743 310 692
Z"/>
<!-- Main silo body, with a tangent-continuous right shoulder. -->
<path d="
M 389 389
C 389 374 447 351 512 351
C 540 351 565 354 580 359
Q 583 360 583 364
L 583 676
L 443 676
L 443 541
C 443 509 426 490 389 470
Z"/>
<!-- Peaked roof with softly tapered, burr-free tips. -->
<path d="
M 512 274
L 647 365
Q 649 370 647 376
C 609 350 563 337 512 337
C 460 337 414 350 377 376
Q 375 370 377 365
Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

+17
View File
@@ -0,0 +1,17 @@
{
"id": "silo",
"name": "Silo",
"version": "RELEASE.2026-08-06T00-00-00Z",
"description": "S3-Compatible Object Storage. A MinIO fork maintained by PGSTY",
"logo": "logo.svg",
"links": {
"github": "https://github.com/pgsty/silo",
"website": "https://silo.pgsty.com",
"docs": "https://silo.pgsty.com/docs/"
},
"tags": [
"s3",
"file",
"storage"
]
}
+18
View File
@@ -0,0 +1,18 @@
[variables]
main_domain = "${domain}"
console_domain = "${domain}"
password = "${password:32}"
[config]
[[config.domains]]
serviceName = "silo"
port = 9000
host = "${main_domain}"
[[config.domains]]
serviceName = "silo"
port = 9001
host = "${console_domain}"
[config.env]
MINIO_ROOT_USER = "silo_usr"
MINIO_ROOT_PASSWORD = "${password}"
+1 -1
View File
@@ -6,7 +6,7 @@
"logo": "image.png",
"links": {
"github": "https://github.com/flomp/wanderer",
"website": "https://wanderer.app",
"website": "https://wanderer.to",
"docs": "https://github.com/flomp/wanderer#readme"
},
"tags": [
+10 -1
View File
@@ -2,13 +2,17 @@ version: "3.8"
services:
wupnext:
image: docker.io/javierlnmn/wupnext:latest
image: docker.io/javierlnmn/wupnext:0.3.0
restart: unless-stopped
environment:
SECRET_KEY: ${SECRET_KEY}
DEBUG: "False"
ALLOWED_HOSTS: ${ALLOWED_HOSTS}
CSRF_TRUSTED_ORIGINS: ${CSRF_TRUSTED_ORIGINS}
# Public base URL, used for links in emails
SITE_URL: ${SITE_URL}
# Public origin the MCP server advertises to clients during discovery
MCP_BASE_URL: ${MCP_BASE_URL}
EMAIL_BACKEND: anymail.backends.resend.EmailBackend
DEFAULT_FROM_EMAIL: ${DEFAULT_FROM_EMAIL}
RESEND_API_KEY: ${RESEND_API_KEY}
@@ -31,9 +35,14 @@ services:
DEBUG: "False"
ALLOWED_HOSTS: ${ALLOWED_HOSTS}
CSRF_TRUSTED_ORIGINS: ${CSRF_TRUSTED_ORIGINS}
SITE_URL: ${SITE_URL}
EMAIL_BACKEND: anymail.backends.resend.EmailBackend
DEFAULT_FROM_EMAIL: ${DEFAULT_FROM_EMAIL}
RESEND_API_KEY: ${RESEND_API_KEY}
# Django Q2 cluster (ORM broker, no Redis)
Q_WORKERS: ${Q_WORKERS}
Q_TIMEOUT: ${Q_TIMEOUT}
Q_RETRY: ${Q_RETRY}
volumes:
- wupnext-db:/app/db
depends_on:
+2 -1
View File
@@ -2,7 +2,7 @@
"id": "wupnext",
"name": "WupNext",
"version": "latest",
"description": "WupNext is a self-hosted to-do and task queue app: groups, subtasks, deadlines, a built-in pomodoro timer, and daily email reminders. Server-rendered Django with HTMX, backed by SQLite.",
"description": "WupNext is a self-hosted to-do and task queue app: groups, subtasks, deadlines, a built-in pomodoro timer, email reminders, and an MCP server so an AI client can work the board. Server-rendered Django with HTMX, backed by SQLite.",
"logo": "logo.svg",
"links": {
"github": "https://github.com/javierlnmn/wupnext",
@@ -14,6 +14,7 @@
"tasks",
"productivity",
"pomodoro",
"mcp",
"django",
"self-hosted"
]
+6 -1
View File
@@ -12,11 +12,16 @@ env = [
"SECRET_KEY=${secret_key}",
"ALLOWED_HOSTS=${main_domain}",
"CSRF_TRUSTED_ORIGINS=https://${main_domain}",
"SITE_URL=https://${main_domain}",
"MCP_BASE_URL=https://${main_domain}",
"DEFAULT_FROM_EMAIL=${from_email}",
"RESEND_API_KEY=${resend_api_key}",
"DJANGO_SUPERUSER_USERNAME=${admin_username}",
"DJANGO_SUPERUSER_EMAIL=${admin_email}",
"DJANGO_SUPERUSER_PASSWORD=${admin_password}"
"DJANGO_SUPERUSER_PASSWORD=${admin_password}",
"Q_WORKERS=2",
"Q_TIMEOUT=60",
"Q_RETRY=120"
]
mounts = []