Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad464c371b | ||
|
|
4cc1cbbe77 | ||
|
|
e38ef3abd5 | ||
|
|
fffa46deac | ||
|
|
1278af50b5 | ||
|
|
816134d4d0 | ||
|
|
34d9dca9ee | ||
|
|
5b41b6bbda | ||
|
|
a9061f61cf | ||
|
|
7a594e8eb1 | ||
|
|
5e4eb93f6b | ||
|
|
cae917a8fd | ||
|
|
3d7dbc0f70 | ||
|
|
ab35b586e1 | ||
|
|
dae4af28be | ||
|
|
c77bd2ca70 | ||
|
|
e3ead8bfa8 | ||
|
|
60fb82046e | ||
|
|
d93f17ed53 | ||
|
|
fad0db2689 | ||
|
|
a33ce26ace | ||
|
|
41841e1441 | ||
|
|
446a0faece | ||
|
|
56bfaea59e | ||
|
|
9cbed1788e | ||
|
|
7a41d04323 | ||
|
|
3b41a21613 | ||
|
|
94e39254bf | ||
|
|
736718b4ff | ||
|
|
1373a7760b | ||
|
|
10fb6bacaf | ||
|
|
a1f497250e | ||
|
|
97648ba37b | ||
|
|
cda0f75a16 | ||
|
|
86861a7c95 | ||
|
|
ac45a0c2db | ||
|
|
b5df69fddb | ||
|
|
51b419dc23 | ||
|
|
386823709e | ||
|
|
68e6a27ab1 | ||
|
|
5efe1e1103 | ||
|
|
c1716ad72b | ||
|
|
5459883b47 | ||
|
|
774850fc8c | ||
|
|
e6c50bedcd | ||
|
|
bd491662af | ||
|
|
6d276ca4a5 | ||
|
|
1f1b900782 | ||
|
|
e0208cd9f3 | ||
|
|
63023253dd | ||
|
|
da2bc238fa | ||
|
|
444fa72771 | ||
|
|
44bceb5b64 | ||
|
|
0bf1113ad7 | ||
|
|
4af1d7b278 | ||
|
|
55411395de | ||
|
|
ca7959dad9 | ||
|
|
11089396dc | ||
|
|
32a7d0ac70 | ||
|
|
c8df2286f9 | ||
|
|
f7dd6b9988 | ||
|
|
0fe93d8e39 | ||
|
|
9551602df7 | ||
|
|
f9ccaf6c34 | ||
|
|
5308f0d04a | ||
|
|
82f8a12b29 | ||
|
|
00b6c160a0 | ||
|
|
77b708e335 | ||
|
|
50da81b19a | ||
|
|
3be5179a10 | ||
|
|
c13cced0f0 | ||
|
|
34fd42e2e5 | ||
|
|
7bb611134c | ||
|
|
4e67eaa911 | ||
|
|
a1a811bbf4 | ||
|
|
eb7994560f | ||
|
|
ea98830477 | ||
|
|
e3c611741d | ||
|
|
e9b1f1585a | ||
|
|
c42e1c3fc8 | ||
|
|
a145a44964 | ||
|
|
851a6601f2 | ||
|
|
1b2e06cb4b | ||
|
|
78f0e98b86 | ||
|
|
259eb8a402 | ||
|
|
a880b39d48 | ||
|
|
ad5a86fc61 | ||
|
|
b7e522a9a0 | ||
|
|
f9c496fa57 | ||
|
|
e65859bced | ||
|
|
e52b86baef | ||
|
|
18c3bf2858 | ||
|
|
bf7e3e9d69 | ||
|
|
35b1f66c7a | ||
|
|
e1c13b9bae | ||
|
|
b21588b34e | ||
|
|
e8d2615c2b | ||
|
|
63adcdadc2 | ||
|
|
50f0b5f9d9 | ||
|
|
8e50aaeaa3 | ||
|
|
aa6354bda0 | ||
|
|
2a725ce0fc | ||
|
|
8d416a8a87 | ||
|
|
b8c5f2bab5 | ||
|
|
914e393f51 | ||
|
|
f1295b6518 | ||
|
|
d718f761b1 | ||
|
|
5a8b088c49 | ||
|
|
91061251de | ||
|
|
4d90eab114 | ||
|
|
bede4dbd80 | ||
|
|
6ae5bb5103 | ||
|
|
e02190ab18 | ||
|
|
d742938c5a | ||
|
|
8f836f5f28 | ||
|
|
aefe3143ce | ||
|
|
543ce0b9a5 | ||
|
|
4fd2873a67 | ||
|
|
df88ec0972 | ||
|
|
4dd732170c | ||
|
|
80a50aa0d0 | ||
|
|
a9e07bf677 | ||
|
|
78e1b776ad | ||
|
|
4699cf0de8 | ||
|
|
7fca9845cf | ||
|
|
e309adc45b | ||
|
|
615f8d8d69 | ||
|
|
271aa8224f | ||
|
|
fa06c0d2e2 | ||
|
|
ef51d95292 | ||
|
|
8a191639b6 | ||
|
|
830d6bbc8d | ||
|
|
1f1e3b4651 | ||
|
|
0179ab425c | ||
|
|
1f7fd68b56 |
@@ -2,7 +2,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
activepieces:
|
||||
image: activepieces/activepieces:0.35.0
|
||||
image: activepieces/activepieces:0.91.2
|
||||
restart: unless-stopped
|
||||
|
||||
depends_on:
|
||||
@@ -32,7 +32,7 @@ services:
|
||||
AP_TEMPLATES_SOURCE_URL: https://cloud.activepieces.com/api/v1/flow-templates
|
||||
|
||||
postgres:
|
||||
image: postgres:14
|
||||
image: postgres:17
|
||||
restart: unless-stopped
|
||||
|
||||
environment:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "activepieces",
|
||||
"name": "Activepieces",
|
||||
"version": "0.35.0",
|
||||
"version": "0.91.2",
|
||||
"description": "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.",
|
||||
"logo": "activepieces.svg",
|
||||
"links": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "appwrite",
|
||||
"name": "Appwrite",
|
||||
"version": "1.9.5",
|
||||
"version": "2.3.0",
|
||||
"description": "Appwrite is an end-to-end platform for building Web, Mobile, Native, or Backend apps, packaged as a set of Docker microservices. It includes both a backend server and a fully integrated hosting solution for deploying static and server-side rendered frontends. Appwrite abstracts the complexity and repetitiveness required to build modern apps from scratch and allows you to build secure, full-stack applications faster.\nUsing Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.",
|
||||
"links": {
|
||||
"github": "https://github.com/appwrite/appwrite",
|
||||
@@ -12,8 +12,7 @@
|
||||
"tags": [
|
||||
"database",
|
||||
"firebase",
|
||||
"mariadb",
|
||||
"mongodb",
|
||||
"postgresql",
|
||||
"hosting",
|
||||
"self-hosted"
|
||||
]
|
||||
|
||||
@@ -2,28 +2,36 @@
|
||||
main_domain = "${domain}"
|
||||
functions_domain = "functions.${main_domain}"
|
||||
sites_domain = "sites.${main_domain}"
|
||||
mqtt_domain = "mqtt.${main_domain}"
|
||||
openssl_key = "${password:32}"
|
||||
db_root_pw = "${password:32}"
|
||||
db_user_pw = "${password:32}"
|
||||
notifications_tracking_secret = "${password:32}"
|
||||
db_pw = "${password:32}"
|
||||
usage_pw = "${password:32}"
|
||||
executor_secret = "${password:32}"
|
||||
jobs_secret = "${password:32}"
|
||||
geo_secret = "${password:32}"
|
||||
|
||||
[config]
|
||||
# Build jobs and runtimes join <appName>_default, which isolated deployments never create
|
||||
# (every service sits on the external <appName> network instead).
|
||||
isolated = false
|
||||
env = [
|
||||
"_APP_ENV=production",
|
||||
"_APP_EDITION=self-hosted",
|
||||
"_APP_LOCALE=en",
|
||||
"_APP_POOL_ADAPTER=stack",
|
||||
"_APP_WORKER_PER_CORE=6",
|
||||
"_APP_OPTIONS_ABUSE=enabled",
|
||||
# Requires HTTPS to be enabled on the main domain in Dokploy (Domains tab).
|
||||
# Makes Appwrite generate https:// URLs (e.g. APPWRITE_FUNCTION_API_ENDPOINT for
|
||||
# Functions) and reject plain-HTTP API calls; over plain HTTP the console cannot log in.
|
||||
"_APP_OPTIONS_FORCE_HTTPS=enabled",
|
||||
"_APP_OPTIONS_ABUSE_INCREASED_LIMIT_PROJECTS=",
|
||||
"_APP_OPTIONS_FORCE_HTTPS=disabled",
|
||||
"_APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled",
|
||||
"_APP_OPTIONS_ROUTER_PROTECTION=disabled",
|
||||
"_APP_ROUTER_AUTO_CERTIFICATES=enabled",
|
||||
"_APP_LOCKING_ENABLED=enabled",
|
||||
"_APP_OPENSSL_KEY_V1=${openssl_key}",
|
||||
"_APP_NOTIFICATIONS_TRACKING_SECRET=${notifications_tracking_secret}",
|
||||
"_APP_DOMAIN=${main_domain}",
|
||||
"_APP_CONSOLE_DOMAIN=${main_domain}",
|
||||
"_APP_CONSOLE_URL=",
|
||||
"_APP_CONSOLE_TRUSTED_PROJECTS=",
|
||||
"_APP_CUSTOM_DOMAIN_DENY_LIST=example.com,test.com,app.example.com",
|
||||
"_APP_DOMAIN_FUNCTIONS=${functions_domain}",
|
||||
@@ -41,6 +49,15 @@ env = [
|
||||
"_APP_CONSOLE_SCHEMA=appwriteio",
|
||||
"_APP_CONSOLE_SESSION_ALERTS=disabled",
|
||||
"_APP_CONSOLE_COUNTRIES_DENYLIST=",
|
||||
"_APP_CONSOLE_GITHUB_APP_ID=",
|
||||
"_APP_CONSOLE_GITHUB_SECRET=",
|
||||
"_APP_CONSOLE_GITLAB_APP_ID=",
|
||||
"_APP_CONSOLE_GITLAB_SECRET=",
|
||||
"_APP_CONSOLE_GITLAB_ENDPOINT=",
|
||||
"_APP_CONSOLE_BITBUCKET_APP_ID=",
|
||||
"_APP_CONSOLE_BITBUCKET_SECRET=",
|
||||
"_APP_CONSOLE_GOOGLE_APP_ID=",
|
||||
"_APP_CONSOLE_GOOGLE_SECRET=",
|
||||
"_APP_SYSTEM_EMAIL_NAME=Appwrite",
|
||||
"_APP_SYSTEM_EMAIL_ADDRESS=noreply@appwrite.io",
|
||||
"_APP_SYSTEM_TEAM_EMAIL=team@appwrite.io",
|
||||
@@ -49,33 +66,52 @@ env = [
|
||||
"_APP_EMAIL_CERTIFICATES=",
|
||||
"_APP_COMPRESSION_ENABLED=enabled",
|
||||
"_APP_COMPRESSION_MIN_SIZE_BYTES=1024",
|
||||
"_APP_USAGE_STATS=enabled",
|
||||
"_APP_USAGE_AGGREGATION_INTERVAL=30",
|
||||
"_APP_STATS_RESOURCES_INTERVAL=30",
|
||||
"_APP_LOGGING_PROVIDER=",
|
||||
"_APP_LOGGING_CONFIG=",
|
||||
"_APP_LOGGING_CONFIG_REALTIME=",
|
||||
"_APP_EXPERIMENT_LOGGING_PROVIDER=",
|
||||
"_APP_EXPERIMENT_LOGGING_CONFIG=",
|
||||
"_APP_LOGGING_FORMAT=pretty",
|
||||
"_APP_GEO_ENDPOINT=http://appwrite-geo/v1",
|
||||
"_APP_GEO_SECRET=${geo_secret}",
|
||||
"_APP_REDIS_HOST=redis",
|
||||
"_APP_REDIS_PORT=6379",
|
||||
"_APP_REDIS_USER=",
|
||||
"_APP_REDIS_PASS=",
|
||||
"_APP_DB_ADAPTER=mongodb",
|
||||
"_APP_DB_HOST=mongodb",
|
||||
"_APP_DB_PORT=27017",
|
||||
"_APP_DB_ADAPTER=postgresql",
|
||||
"_APP_DB_HOST=postgresql",
|
||||
"_APP_DB_PORT=5432",
|
||||
"_APP_DB_SCHEMA=appwrite",
|
||||
"_APP_DB_USER=user",
|
||||
"_APP_DB_PASS=${db_user_pw}",
|
||||
"_APP_DB_ROOT_PASS=${db_root_pw}",
|
||||
"_APP_DB_PASS=${db_pw}",
|
||||
"_APP_DATABASE_SHARED_TABLES=",
|
||||
"_APP_DATABASE_SHARED_NAMESPACE=",
|
||||
"_APP_DB_ADAPTER_VECTORSDB=",
|
||||
"_APP_DB_HOST_VECTORSDB=",
|
||||
"_APP_DB_PORT_VECTORSDB=",
|
||||
"_APP_LIMIT_DATABASE_BATCH=100",
|
||||
"_APP_DOCUMENTSDB=disabled",
|
||||
"_APP_DB_ADAPTER_DOCUMENTSDB=mongodb",
|
||||
"_APP_DB_HOST_DOCUMENTSDB=mongodb",
|
||||
"_APP_DB_PORT_DOCUMENTSDB=27017",
|
||||
"_APP_DB_SCHEMA_DOCUMENTSDB=",
|
||||
"_APP_DB_USER_DOCUMENTSDB=",
|
||||
"_APP_DB_PASS_DOCUMENTSDB=",
|
||||
"_APP_CONNECTIONS_DATABASE_DOCUMENTSDB=",
|
||||
"_APP_VECTORSDB=disabled",
|
||||
"_APP_DB_ADAPTER_VECTORSDB=postgresql",
|
||||
"_APP_DB_HOST_VECTORSDB=postgresql",
|
||||
"_APP_DB_PORT_VECTORSDB=5432",
|
||||
"_APP_DB_SCHEMA_VECTORSDB=",
|
||||
"_APP_DB_USER_VECTORSDB=",
|
||||
"_APP_DB_PASS_VECTORSDB=",
|
||||
"_APP_CONNECTIONS_DATABASE_VECTORSDB=",
|
||||
"_APP_EMBEDDING=disabled",
|
||||
"_APP_USAGE_STATS=enabled",
|
||||
"_APP_USAGE_PASS=${usage_pw}",
|
||||
"_APP_CONNECTIONS_DB_USAGE=http://appwrite:${usage_pw}@clickhouse:8123/appwrite",
|
||||
"_APP_POOL_SIZE_USAGE=2",
|
||||
"_APP_CONNECTIONS_DB_EXECUTIONS=http://appwrite:${usage_pw}@clickhouse:8123/appwrite",
|
||||
"_APP_POOL_SIZE_EXECUTIONS=2",
|
||||
"_APP_MAINTENANCE_RETENTION_USAGE_TTL=180",
|
||||
"_APP_STATS_USAGE_QUEUE_NAME=v1-stats-usage",
|
||||
"_APP_STATS_RESOURCES_QUEUE_NAME=v1-stats-resources",
|
||||
"_APP_STATS_RESOURCES_INTERVAL=3600",
|
||||
"_APP_MQTT_REPLAY_DEPTH=5",
|
||||
"_APP_MQTT_MAX_PACKET_SIZE=64000",
|
||||
"_APP_SMTP_HOST=",
|
||||
"_APP_SMTP_PORT=",
|
||||
"_APP_SMTP_SECURE=",
|
||||
@@ -84,8 +120,10 @@ env = [
|
||||
"_APP_SMS_PROVIDER=",
|
||||
"_APP_SMS_FROM=",
|
||||
"_APP_SMS_PROJECTS_DENY_LIST=",
|
||||
"_APP_PWNED_PASSWORDS_DSN=none://localhost",
|
||||
"_APP_STORAGE_LIMIT=30000000",
|
||||
"_APP_STORAGE_PREVIEW_LIMIT=20000000",
|
||||
"_APP_AUTOGRAVITY_HOST=http://appwrite-autogravity:8080",
|
||||
"_APP_STORAGE_ANTIVIRUS=disabled",
|
||||
"_APP_STORAGE_ANTIVIRUS_HOST=clamav",
|
||||
"_APP_STORAGE_ANTIVIRUS_PORT=3310",
|
||||
@@ -112,21 +150,26 @@ env = [
|
||||
"_APP_STORAGE_WASABI_REGION=eu-central-1",
|
||||
"_APP_STORAGE_WASABI_BUCKET=",
|
||||
"_APP_COMPUTE_SIZE_LIMIT=30000000",
|
||||
"_APP_COMPUTE_BUILD_TIMEOUT=900",
|
||||
"_APP_COMPUTE_BUILD_SIZE_LIMIT=2000000000",
|
||||
"_APP_COMPUTE_BUILD_TIMEOUT=2700",
|
||||
"_APP_COMPUTE_CPUS=0",
|
||||
"_APP_COMPUTE_MEMORY=0",
|
||||
"_APP_COMPUTE_INACTIVE_THRESHOLD=60",
|
||||
"_APP_COMPUTE_MAINTENANCE_INTERVAL=3600",
|
||||
"_APP_COMPUTE_RUNTIMES_NETWORK=dokploy-network",
|
||||
"_APP_OPEN_RUNTIMES_NFT=enabled",
|
||||
"_APP_FUNCTIONS_TIMEOUT=900",
|
||||
"_APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000",
|
||||
"_APP_FUNCTIONS_SCHEDULE_SPREAD=0",
|
||||
"_APP_FUNCTIONS_RUNTIMES=node-22,python-3.12,php-8.3,ruby-3.3,dart-3.5,go-1.23,rust-1.83,bun-1.1,deno-1.46,java-21.0,dotnet-8.0",
|
||||
"_APP_SITES_TIMEOUT=900",
|
||||
"_APP_SITES_RUNTIMES=static-1,node-22",
|
||||
"_APP_EXECUTOR_SECRET=${executor_secret}",
|
||||
"_APP_EXECUTOR_HOST=http://openruntimes-executor/v1",
|
||||
"_APP_EXECUTOR_IMAGES=openruntimes/node:v5-22,openruntimes/python:v5-3.12,openruntimes/php:v5-8.3,openruntimes/static:v5-1",
|
||||
"_APP_OPEN_RUNTIMES_NFT=enabled",
|
||||
"_APP_EXECUTOR_CONNECTION_STORAGE=local://localhost",
|
||||
"_APP_JOBS_HOST=http://orchestrator:8080",
|
||||
"_APP_JOBS_SECRET=${jobs_secret}",
|
||||
"_APP_JOBS_ENDPOINT=http://appwrite",
|
||||
"_APP_BROWSER_HOST=http://appwrite-browser:3000/v1",
|
||||
"_APP_WORKER_SCREENSHOTS_ROUTER=http://appwrite",
|
||||
"_APP_DOCKER_HUB_USERNAME=",
|
||||
@@ -138,7 +181,6 @@ env = [
|
||||
"_APP_MAINTENANCE_RETENTION_AUDIT=1209600",
|
||||
"_APP_MAINTENANCE_RETENTION_AUDIT_CONSOLE=15778800",
|
||||
"_APP_MAINTENANCE_RETENTION_ABUSE=86400",
|
||||
"_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000",
|
||||
"_APP_MAINTENANCE_RETENTION_SCHEDULES=86400",
|
||||
"_APP_INTERVAL_DOMAIN_VERIFICATION=120",
|
||||
"_APP_INTERVAL_CLEANUP_STALE_EXECUTIONS=300",
|
||||
@@ -153,60 +195,50 @@ env = [
|
||||
"_APP_VCS_GITHUB_CLIENT_ID=",
|
||||
"_APP_VCS_GITHUB_CLIENT_SECRET=",
|
||||
"_APP_VCS_GITHUB_WEBHOOK_SECRET=",
|
||||
"_APP_VCS_GITEA_ENDPOINT=",
|
||||
"_APP_VCS_GITEA_BROWSER_ENDPOINT=",
|
||||
"_APP_VCS_GITEA_CLIENT_ID=",
|
||||
"_APP_VCS_GITEA_CLIENT_SECRET=",
|
||||
"_APP_VCS_GITEA_WEBHOOK_SECRET=",
|
||||
"_APP_VCS_GITLAB_ENDPOINT=",
|
||||
"_APP_VCS_GITLAB_CLIENT_ID=",
|
||||
"_APP_VCS_GITLAB_CLIENT_SECRET=",
|
||||
"_APP_VCS_GITLAB_WEBHOOK_SECRET=",
|
||||
"_APP_VCS_BITBUCKET_CLIENT_ID=",
|
||||
"_APP_VCS_BITBUCKET_CLIENT_SECRET=",
|
||||
"_APP_VCS_BITBUCKET_WEBHOOK_SECRET=",
|
||||
"_APP_VCS_ORIGIN_CLIENT_ID=",
|
||||
"_APP_VCS_ORIGIN_PRIVATE_KEY=",
|
||||
"_APP_VCS_WEBHOOK_URL=",
|
||||
"_APP_MIGRATION_HOST=appwrite",
|
||||
"_APP_MIGRATIONS_FIREBASE_CLIENT_ID=",
|
||||
"_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=",
|
||||
"_APP_ASSISTANT_OPENAI_API_KEY=",
|
||||
]
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "mongo-init.js"
|
||||
content = """// mongo-init.js
|
||||
|
||||
// Switch to the admin database
|
||||
const adminDb = db.getSiblingDB('admin');
|
||||
|
||||
// Get username and password from environment variables
|
||||
const username = process.env.MONGO_INITDB_USERNAME;
|
||||
const password = process.env.MONGO_INITDB_PASSWORD;
|
||||
const database = process.env.MONGO_INITDB_DATABASE;
|
||||
|
||||
// Create the user
|
||||
adminDb.createUser({
|
||||
user: username,
|
||||
pwd: password,
|
||||
roles: [
|
||||
{ role: 'readWrite', db: database }
|
||||
]
|
||||
});
|
||||
"""
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "mongo-entrypoint.sh"
|
||||
content = """#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Fix keyfile permissions if mounted from volume
|
||||
KEYFILE_PATH="/data/keyfile/mongo-keyfile"
|
||||
|
||||
if [ ! -f "$KEYFILE_PATH" ]; then
|
||||
echo "Generating random MongoDB keyfile..."
|
||||
mkdir -p /data/keyfile
|
||||
openssl rand -base64 756 > "$KEYFILE_PATH"
|
||||
fi
|
||||
|
||||
chmod 400 "$KEYFILE_PATH"
|
||||
chown mongodb:mongodb "$KEYFILE_PATH" 2>/dev/null || chown 999:999 "$KEYFILE_PATH"
|
||||
|
||||
# Use MongoDB's standard entrypoint with our command
|
||||
exec docker-entrypoint.sh mongod --replSet rs0 --bind_ip_all --auth --keyFile "$KEYFILE_PATH"
|
||||
"""
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite-console"
|
||||
port = 3000
|
||||
host = "${main_domain}"
|
||||
path = "/"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
path = "/"
|
||||
path = "/v1"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
path = "/.well-known"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite-realtime"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
path = "/v1/realtime"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite"
|
||||
@@ -221,13 +253,7 @@ host = "${functions_domain}"
|
||||
path = "/"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite-console"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
path = "/console"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "appwrite-realtime"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
path = "/v1/realtime"
|
||||
serviceName = "appwrite-mqtt"
|
||||
port = 8083
|
||||
host = "${mqtt_domain}"
|
||||
path = "/"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
services:
|
||||
autobase-console:
|
||||
image: autobase/console:2.10.0
|
||||
image: autobase/console:2.11.0
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- "80"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "autobase",
|
||||
"name": "Autobase",
|
||||
"version": "2.10.0",
|
||||
"version": "2.11.0",
|
||||
"description": "Autobase for PostgreSQL® is an open-source alternative to cloud-managed databases (self-hosted DBaaS).",
|
||||
"links": {
|
||||
"github": "https://github.com/vitabaks/autobase",
|
||||
|
||||
@@ -2,7 +2,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
bentopdf:
|
||||
image: bentopdf/bentopdf:latest
|
||||
image: ghcr.io/alam00000/bentopdf-simple:sha-72d20bb
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- 80
|
||||
- 8080
|
||||
|
||||
@@ -4,6 +4,6 @@ main_domain = "${domain}"
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "bentopdf"
|
||||
port = 80
|
||||
port = 8080
|
||||
host = "${main_domain}"
|
||||
path = "/"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" width="400" height="400" stroke-width="18" stroke="#fff" fill="#595ce7">
|
||||
<path fill-rule="evenodd" d="m200 390c-105.1 0-190-84.9-190-190 0-105.1 84.9-190 190-190 105.1 0 190 84.9 190 190 0 105.1-84.9 190-190 190z"/>
|
||||
<path fill-rule="evenodd" d="m262.6 368.6c-65.2 0-117.9-75.4-117.9-168.7 0-93.3 52.7-168.7 117.9-168.7 65.1 0 117.8 75.4 117.8 168.7 0 93.3-52.7 168.7-117.8 168.7z"/>
|
||||
<path fill-rule="evenodd" d="m319.9 330.7c-31.8 0-57.4-58.2-57.4-130.2 0-72 25.6-130.1 57.4-130.1 31.7 0 57.3 58.1 57.3 130.1 0 72-25.6 130.2-57.3 130.2z"/>
|
||||
<path fill="#fff" fill-rule="evenodd" d="m364.5 267.5c-14.1 0-25.6-29.9-25.6-67 0-37 11.5-67 25.6-67 14.1 0 25.5 30 25.5 67 0 37.1-11.4 67-25.5 67z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 768 B |
@@ -0,0 +1,85 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
corsfix:
|
||||
image: ghcr.io/corsfix/corsfix-app:8899bb15e71c7238285ccefc4c95a0cd47daeaf0
|
||||
platform: linux/amd64
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- "3000"
|
||||
environment:
|
||||
MONGODB_URI: mongodb://corsfix:${MONGODB_PASSWORD}@mongodb:27017/db?authSource=admin
|
||||
REDIS_URI: redis://default:${REDIS_PASSWORD}@redis:6379
|
||||
KEK_VERSION: KEK_VERSION_1
|
||||
KEK_VERSION_1: ${KEK_VERSION_1}
|
||||
AUTH_SECRET: ${AUTH_SECRET}
|
||||
AUTH_TRUST_HOST: "true"
|
||||
AUTH_URL: ${AUTH_URL}
|
||||
PROXY_DOMAIN: ${PROXY_DOMAIN}
|
||||
DISABLE_SIGNUP: ${DISABLE_SIGNUP:-false}
|
||||
depends_on:
|
||||
mongodb:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
corsfix-proxy:
|
||||
image: ghcr.io/corsfix/corsfix-proxy:8899bb15e71c7238285ccefc4c95a0cd47daeaf0
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- "80"
|
||||
environment:
|
||||
MONGODB_URI: mongodb://corsfix:${MONGODB_PASSWORD}@mongodb:27017/db?authSource=admin
|
||||
REDIS_URI: redis://default:${REDIS_PASSWORD}@redis:6379
|
||||
KEK_VERSION_1: ${KEK_VERSION_1}
|
||||
APP_DOMAIN: ${APP_DOMAIN}
|
||||
RPM: ${RPM:-180}
|
||||
ALLOWED_ORIGINS: ${ALLOWED_ORIGINS:-}
|
||||
ALLOWED_TARGETS: ${ALLOWED_TARGETS:-}
|
||||
depends_on:
|
||||
mongodb:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
mongodb:
|
||||
image: mongo:8.0.17
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: corsfix
|
||||
MONGO_INITDB_ROOT_PASSWORD: ${MONGODB_PASSWORD}
|
||||
MONGO_INITDB_DATABASE: db
|
||||
volumes:
|
||||
- mongodb-data:/data/db
|
||||
- mongodb-config:/data/configdb
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD-SHELL
|
||||
- >-
|
||||
mongosh --quiet --username "$${MONGO_INITDB_ROOT_USERNAME}"
|
||||
--password "$${MONGO_INITDB_ROOT_PASSWORD}" --authenticationDatabase admin
|
||||
--eval 'quit(db.adminCommand({ ping: 1 }).ok ? 0 : 1)'
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 30s
|
||||
|
||||
redis:
|
||||
image: redis:7.4.11-alpine
|
||||
restart: unless-stopped
|
||||
command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}", "--appendonly", "yes"]
|
||||
environment:
|
||||
REDIS_PASSWORD: ${REDIS_PASSWORD}
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "REDISCLI_AUTH=\"$${REDIS_PASSWORD}\" redis-cli ping | grep -q PONG"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
mongodb-data:
|
||||
mongodb-config:
|
||||
redis-data:
|
||||
@@ -0,0 +1,27 @@
|
||||
# Corsfix
|
||||
|
||||
This template deploys the Corsfix dashboard and proxy with MongoDB and Redis.
|
||||
The dashboard image requires an **x86-64 / amd64 server**.
|
||||
|
||||
## Before deploying
|
||||
|
||||
1. Dokploy imports template domains without a certificate configuration, so `AUTH_URL` defaults to `http://` followed by the generated dashboard hostname. When enabling **HTTPS** and **Let's Encrypt** in the **Domains** tab, also change `AUTH_URL` in **Environment** to `https://` followed by that hostname and redeploy. This URL must match the public dashboard address, even though Dokploy terminates TLS at the edge. Enable HTTPS for **both** domains before using the playground, which uses HTTPS proxy URLs.
|
||||
2. If using custom domains, point both DNS records to your server, update the domain entries, and set `APP_DOMAIN` and `PROXY_DOMAIN` in **Environment** to the matching hostnames, without `https://` or a trailing slash. Update `AUTH_URL` to the full public dashboard URL, including its `http://` or `https://` scheme.
|
||||
3. Deploy, then open the dashboard domain (the `corsfix` service on port `3000`) and create your account with an email address and password. Self-hosted signup does not require an email server.
|
||||
4. Set `DISABLE_SIGNUP=true` in **Environment** and redeploy after creating the accounts you need.
|
||||
|
||||
## Using the proxy
|
||||
|
||||
The second domain routes to `corsfix-proxy` on port `80`. Use the dashboard to register your application's origin and permitted target domains, manage secrets, and try requests in the playground.
|
||||
|
||||
`RPM` sets the self-hosted rate limit (default: `180` requests per minute). Optional `ALLOWED_ORIGINS` and `ALLOWED_TARGETS` accept comma-separated hostnames for environment-based allowlists; leave them empty to manage applications through the dashboard.
|
||||
|
||||
## Persistent data and upgrades
|
||||
|
||||
MongoDB stores users, applications, and encrypted secrets in named volumes. Redis uses a named volume with append-only persistence. Preserve these volumes and the generated environment values when upgrading.
|
||||
|
||||
Keep a backup of `KEK_VERSION_1`: changing or losing this encryption key makes existing saved secrets unreadable. Do not re-import the template to upgrade an existing installation, because imports generate new credentials and keys. Update the image tags in the existing Compose configuration instead. Changing `MONGODB_PASSWORD` alone does not change the password already stored in MongoDB.
|
||||
|
||||
The Corsfix images are pinned to the same upstream commit because the project publishes commit tags rather than numbered releases.
|
||||
|
||||
See the [Corsfix self-hosting documentation](https://corsfix.com/docs/open-source/self-hosting) for more information.
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "corsfix",
|
||||
"name": "Corsfix",
|
||||
"version": "8899bb15e71c7238285ccefc4c95a0cd47daeaf0",
|
||||
"description": "Corsfix is an open source CORS proxy platform for fetching APIs from browser applications, with origin and target allowlists, encrypted API secrets, response caching, and a management dashboard.",
|
||||
"logo": "corsfix.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/corsfix/corsfix",
|
||||
"website": "https://corsfix.com",
|
||||
"docs": "https://corsfix.com/docs/open-source/self-hosting"
|
||||
},
|
||||
"tags": ["proxy", "developer-tools", "networking"]
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
[variables]
|
||||
app_domain = "${domain}"
|
||||
proxy_domain = "${domain}"
|
||||
mongodb_password = "${hash:64}"
|
||||
redis_password = "${hash:64}"
|
||||
kek_version_1 = "${base64:32}"
|
||||
auth_secret = "${base64:32}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"APP_DOMAIN=${app_domain}",
|
||||
"AUTH_URL=http://${app_domain}",
|
||||
"PROXY_DOMAIN=${proxy_domain}",
|
||||
"MONGODB_PASSWORD=${mongodb_password}",
|
||||
"REDIS_PASSWORD=${redis_password}",
|
||||
"KEK_VERSION_1=${kek_version_1}",
|
||||
"AUTH_SECRET=${auth_secret}",
|
||||
"DISABLE_SIGNUP=false",
|
||||
"RPM=180",
|
||||
"ALLOWED_ORIGINS=",
|
||||
"ALLOWED_TARGETS=",
|
||||
]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "corsfix"
|
||||
port = 3000
|
||||
host = "${app_domain}"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "corsfix-proxy"
|
||||
port = 80
|
||||
host = "${proxy_domain}"
|
||||
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,27 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
databasement:
|
||||
image: davidcrty/databasement:1.8.4
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- 2226
|
||||
environment:
|
||||
- APP_KEY=${APP_KEY}
|
||||
- APP_URL=${APP_URL}
|
||||
- DB_CONNECTION=sqlite
|
||||
- DB_DATABASE=/data/database.sqlite
|
||||
- ENABLE_QUEUE_WORKER=true
|
||||
- TRUSTED_PROXIES=*
|
||||
- LOG_CHANNEL=stderr
|
||||
- APP_DISPLAY_TIMEZONE=${APP_DISPLAY_TIMEZONE}
|
||||
volumes:
|
||||
- databasement-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:2226/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
volumes:
|
||||
databasement-data:
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"id": "databasement",
|
||||
"name": "Databasement",
|
||||
"version": "1.8.4",
|
||||
"description": "Self-hosted database backup manager with a web UI. Schedule, backup, and restore MySQL, PostgreSQL, MariaDB, Microsoft SQL Server, MongoDB, SQLite, Firebird and Redis to S3, SFTP, Samba or local storage. SSH tunnel support.",
|
||||
"logo": "databasement.png",
|
||||
"links": {
|
||||
"github": "https://github.com/David-Crty/databasement",
|
||||
"website": "https://david-crty.github.io/databasement/",
|
||||
"docs": "https://david-crty.github.io/databasement/self-hosting/docker"
|
||||
},
|
||||
"tags": [
|
||||
"backup",
|
||||
"database",
|
||||
"mysql",
|
||||
"postgres",
|
||||
"mongodb"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
app_key = "${base64:32}"
|
||||
timezone = "UTC"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"APP_KEY=base64:${app_key}",
|
||||
"APP_URL=https://${main_domain}",
|
||||
"APP_DISPLAY_TIMEZONE=${timezone}",
|
||||
"TRUSTED_PROXIES=*",
|
||||
"LOG_CHANNEL=stderr",
|
||||
"DB_CONNECTION=sqlite",
|
||||
"DB_DATABASE=/data/database.sqlite",
|
||||
"ENABLE_QUEUE_WORKER=true"
|
||||
]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "databasement"
|
||||
port = 2226
|
||||
host = "${main_domain}"
|
||||
@@ -0,0 +1,16 @@
|
||||
services:
|
||||
dexio:
|
||||
image: ghcr.io/dexio-wiki/dexio:1.0.0
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- DEXIO_PUBLIC_URL=${DEXIO_PUBLIC_URL}
|
||||
- DEXIO_SECRET_KEY=${DEXIO_SECRET_KEY}
|
||||
- DEXIO_DB=/data/dexio.db
|
||||
- DEXIO_FILES_DIR=/data/files
|
||||
expose:
|
||||
- 8080
|
||||
volumes:
|
||||
- dexio-data:/data
|
||||
|
||||
volumes:
|
||||
dexio-data:
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-769 -852 1567 1567"><!-- Dexio mark: a small graph, hub and four nodes. Built by brand/build.py. --><rect x="-769" y="-852" width="1567" height="1567" rx="345" fill="#000000"/><path d="M79.9 0.0L20.4 -475.2L-20.4 -475.2L-79.9 0.0ZM29.1 74.4L498.1 -173.2L483.3 -211.2L-29.1 -74.4ZM-52.3 60.3L392.4 367.4L419.0 336.6L52.3 -60.3ZM-33.6 -72.5L-462.2 191.8L-445.0 228.8L33.6 72.5Z" fill="#ffffff"/><circle cx="0.0" cy="-475.2" r="87.6" fill="#ffffff"/><circle cx="490.7" cy="-192.2" r="87.6" fill="#ffffff"/><circle cx="405.7" cy="352.0" r="74.5" fill="#ffffff"/><circle cx="-453.6" cy="210.3" r="96.4" fill="#ffffff"/><circle cx="0.0" cy="0.0" r="166.5" fill="#38bdf8"/></svg>
|
||||
|
After Width: | Height: | Size: 723 B |
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"id": "dexio",
|
||||
"name": "Dexio",
|
||||
"version": "1.0.0",
|
||||
"description": "One wiki for all your agents. AI agents read and write linked markdown pages over MCP from any machine, and people see what they know in a web app: the link graph, every page and its history.",
|
||||
"logo": "logo.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/dexio-wiki/dexio",
|
||||
"website": "https://dexio.wiki",
|
||||
"docs": "https://github.com/dexio-wiki/dexio/blob/main/deploy/README.md"
|
||||
},
|
||||
"tags": [
|
||||
"wiki",
|
||||
"knowledge-base",
|
||||
"markdown",
|
||||
"mcp",
|
||||
"ai",
|
||||
"agents"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
secret_key = "${password:64}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"DEXIO_PUBLIC_URL=https://${main_domain}",
|
||||
"DEXIO_SECRET_KEY=${secret_key}",
|
||||
]
|
||||
|
||||
# Dexio's session cookie is HTTPS-only, so the domain asks for a certificate.
|
||||
[[config.domains]]
|
||||
serviceName = "dexio"
|
||||
port = 8080
|
||||
host = "${main_domain}"
|
||||
https = true
|
||||
certificateType = "letsencrypt"
|
||||
@@ -0,0 +1,253 @@
|
||||
# Ethora Core as a Dokploy template. GENERATED FILE: edit the bundle's
|
||||
# deploy/compose/docker-compose.yml and run build-template.sh.
|
||||
#
|
||||
# Same services and start order as the bundle (config -> databases -> xmpp ->
|
||||
# api, jobs -> init -> frontend), without the bundled Caddy: Dokploy's Traefik
|
||||
# routes the four public hosts declared in template.toml. The scripts and
|
||||
# templates are the template's mounts, which Dokploy writes under ../files/.
|
||||
# Every variable comes from the .env Dokploy writes next to this file
|
||||
# (template.toml, [config] env). See README.md in this directory.
|
||||
|
||||
x-logging: &logging
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "5"
|
||||
|
||||
x-api: &api
|
||||
image: ${ETHORA_API_IMAGE:-docker.io/dappros/ethora-api:2610}
|
||||
entrypoint: ["tini", "--", "bash", "/ethora/scripts/api-entrypoint.sh"]
|
||||
volumes:
|
||||
- config:/ethora/config:ro
|
||||
- ../files/scripts:/ethora/scripts:ro
|
||||
<<: *logging
|
||||
|
||||
services:
|
||||
# ---------------------------------------------------------------- config --
|
||||
config:
|
||||
image: ${ETHORA_XMPP_IMAGE:-docker.io/dappros/ethora-xmpp:2610}
|
||||
user: "0:0"
|
||||
entrypoint: ["/bin/sh", "/ethora/scripts/render-config.sh"]
|
||||
# Every value in .env reaches the renderer, so any variable of the
|
||||
# installer's templates (POSTMARK_*, EMAIL_TLD_*, RATE_LIMIT_*, ...) can
|
||||
# be set there. The hosts are derived here once, for caddy and xmpp too.
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
environment:
|
||||
ROOT_DOMAIN: ${ROOT_DOMAIN:?ROOT_DOMAIN is not set; run ./configure.sh or fill in .env}
|
||||
API_DOMAIN: ${API_DOMAIN:-api.${ROOT_DOMAIN}}
|
||||
WEB_DOMAIN: ${WEB_DOMAIN:-app.${ROOT_DOMAIN}}
|
||||
XMPP_DOMAIN: ${XMPP_DOMAIN:-xmpp.${ROOT_DOMAIN}}
|
||||
FILES_DOMAIN: ${FILES_DOMAIN:-files.${ROOT_DOMAIN}}
|
||||
volumes:
|
||||
- config:/out/config
|
||||
- mysql-initdb:/out/mysql-initdb
|
||||
- ../files/scripts:/ethora/scripts:ro
|
||||
- ../files/templates:/ethora/templates:ro
|
||||
restart: "no"
|
||||
<<: *logging
|
||||
|
||||
# ------------------------------------------------------------- databases --
|
||||
mongo:
|
||||
image: mongo:6.0.8
|
||||
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
|
||||
volumes:
|
||||
- mongo:/data/db
|
||||
- mongo-config:/data/configdb
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
# A TCP connect, not mongosh: see docker-compose.enterprise.yml.
|
||||
test: ["CMD", "bash", "-c", "exec 3<>/dev/tcp/127.0.0.1/27017"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
<<: *logging
|
||||
|
||||
mongo-init:
|
||||
image: mongo:6.0.8
|
||||
entrypoint: ["bash", "/ethora/scripts/mongo-init.sh"]
|
||||
volumes:
|
||||
- ../files/scripts:/ethora/scripts:ro
|
||||
depends_on:
|
||||
mongo:
|
||||
condition: service_healthy
|
||||
restart: "no"
|
||||
<<: *logging
|
||||
|
||||
mysql:
|
||||
image: mysql:8.1.0
|
||||
command: ["--default-authentication-plugin=mysql_native_password"]
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:?MYSQL_ROOT_PASSWORD is not set}
|
||||
# ejabberd connects as root from its own container (as on a host install).
|
||||
MYSQL_ROOT_HOST: "%"
|
||||
volumes:
|
||||
- mysql:/var/lib/mysql
|
||||
- mysql-initdb:/docker-entrypoint-initdb.d:ro
|
||||
depends_on:
|
||||
config:
|
||||
condition: service_completed_successfully
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -uroot -p\"$$MYSQL_ROOT_PASSWORD\" --silent"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 30s
|
||||
<<: *logging
|
||||
|
||||
redis:
|
||||
image: redis:7.4-alpine
|
||||
volumes:
|
||||
- redis:/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
<<: *logging
|
||||
|
||||
minio:
|
||||
# Our unmodified copy of the MinIO release every install runs (AGPL-3.0);
|
||||
# MinIO withdrew its public images. Set MINIO_IMAGE for a private mirror.
|
||||
image: ${MINIO_IMAGE:-docker.io/dappros/minio:RELEASE.2025-09-07T16-13-09Z}
|
||||
command: ["server", "/data"]
|
||||
environment:
|
||||
MINIO_ROOT_USER: ${MINIO_ROOT_USER:?MINIO_ROOT_USER is not set}
|
||||
MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD:?MINIO_ROOT_PASSWORD is not set}
|
||||
volumes:
|
||||
- minio:/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:9000/minio/health/live"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
<<: *logging
|
||||
|
||||
centrifugo:
|
||||
image: centrifugo/centrifugo:v6
|
||||
command: ["centrifugo", "-c", "/ethora/config/centrifugo/config.json"]
|
||||
volumes:
|
||||
- config:/ethora/config:ro
|
||||
depends_on:
|
||||
config:
|
||||
condition: service_completed_successfully
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 65535
|
||||
hard: 65535
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:8000/health || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
<<: *logging
|
||||
|
||||
# ------------------------------------------------------------------ xmpp --
|
||||
xmpp:
|
||||
image: ${ETHORA_XMPP_IMAGE:-docker.io/dappros/ethora-xmpp:2610}
|
||||
entrypoint: ["/bin/sh", "/ethora/scripts/xmpp-start.sh"]
|
||||
environment:
|
||||
XMPP_DOMAIN: ${XMPP_DOMAIN:-xmpp.${ROOT_DOMAIN}}
|
||||
XMPP_ADMIN_PASSWORD: ${XMPP_ADMIN_PASSWORD:?XMPP_ADMIN_PASSWORD is not set}
|
||||
volumes:
|
||||
- config:/ethora/config:ro
|
||||
- ../files/scripts:/ethora/scripts:ro
|
||||
# Native XMPP clients (port 5222) are not exposed by default; web and SDK
|
||||
# clients connect over wss://xmpp.<root>/ws through caddy. See README.md.
|
||||
depends_on:
|
||||
config:
|
||||
condition: service_completed_successfully
|
||||
mysql:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
# Healthy once ejabberd runs AND the admin account is ensured, which is
|
||||
# what the API and init need.
|
||||
test: ["CMD-SHELL", "test -f /tmp/ethora-admin-ready && /opt/ejabberd/bin/ejabberdctl status >/dev/null"]
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 30
|
||||
start_period: 30s
|
||||
<<: *logging
|
||||
|
||||
# ------------------------------------------------------------------- api --
|
||||
api:
|
||||
<<: *api
|
||||
command: ["api"]
|
||||
depends_on:
|
||||
config:
|
||||
condition: service_completed_successfully
|
||||
mongo-init:
|
||||
condition: service_completed_successfully
|
||||
redis:
|
||||
condition: service_healthy
|
||||
minio:
|
||||
condition: service_healthy
|
||||
xmpp:
|
||||
condition: service_started
|
||||
restart: unless-stopped
|
||||
# Healthcheck: the image's own (GET /v1/ping).
|
||||
|
||||
jobs:
|
||||
<<: *api
|
||||
command: ["jobs"]
|
||||
depends_on:
|
||||
api:
|
||||
condition: service_healthy
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
disable: true
|
||||
|
||||
init:
|
||||
<<: *api
|
||||
command: ["init"]
|
||||
depends_on:
|
||||
api:
|
||||
condition: service_healthy
|
||||
xmpp:
|
||||
condition: service_healthy
|
||||
restart: "no"
|
||||
healthcheck:
|
||||
disable: true
|
||||
|
||||
# End-to-end check through the public URLs (scripts/verify.js):
|
||||
# docker compose --profile verify run --rm verify
|
||||
verify:
|
||||
<<: *api
|
||||
profiles: ["verify"]
|
||||
command: ["verify"]
|
||||
restart: "no"
|
||||
healthcheck:
|
||||
disable: true
|
||||
|
||||
# -------------------------------------------------------------- frontend --
|
||||
frontend:
|
||||
image: ${ETHORA_FRONTEND_IMAGE:-docker.io/dappros/ethora-frontend:2610}
|
||||
entrypoint: ["/bin/sh", "/ethora/scripts/frontend-start.sh"]
|
||||
command: ["serve"]
|
||||
volumes:
|
||||
- config:/ethora/config:ro
|
||||
- ../files/scripts:/ethora/scripts:ro
|
||||
depends_on:
|
||||
config:
|
||||
condition: service_completed_successfully
|
||||
restart: unless-stopped
|
||||
<<: *logging
|
||||
|
||||
volumes:
|
||||
# Your data. Survive `docker compose down`; `down -v` deletes them.
|
||||
mongo:
|
||||
mysql:
|
||||
minio:
|
||||
redis:
|
||||
# Regenerated on every start; safe to delete.
|
||||
mongo-config:
|
||||
config:
|
||||
mysql-initdb:
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.86612 16.7203C6.86612 11.5765 11.036 7.40666 16.1798 7.40666H29.941C30.6313 7.40666 31.191 7.9663 31.191 8.65666C31.191 12.8786 30.0116 15.9059 28.0999 18C26.2039 20.0767 23.7131 21.0979 21.3351 21.5628C18.9587 22.0273 16.6127 21.9536 14.884 21.773C14.0858 21.6896 13.4086 21.5822 12.9067 21.4898L9.38309 28.0216C9.52342 29.1887 10.517 30.0932 11.7219 30.0932H29.941C30.6313 30.0932 31.191 30.6529 31.191 31.3432C31.191 32.0336 30.6313 32.5932 29.941 32.5932H11.7219C9.04014 32.5932 6.86612 30.4192 6.86612 27.7374V16.7203ZM9.36612 22.7875L11.1495 19.4816C11.4215 18.9773 12.0015 18.7226 12.5568 18.8634L12.5579 18.8637L12.5626 18.8648L12.592 18.872C12.6198 18.8787 12.6635 18.8889 12.7221 18.9021C12.8393 18.9284 13.0159 18.9662 13.2434 19.0094C13.6987 19.096 14.3548 19.2041 15.1438 19.2865C16.7322 19.4525 18.8091 19.5092 20.8555 19.1092C22.9004 18.7095 24.8323 17.8712 26.2536 16.3144C27.4955 14.954 28.4459 12.9372 28.6501 9.90666H16.1798C12.4167 9.90666 9.36612 12.9572 9.36612 16.7203V22.7875Z" fill="#0052CD"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.786 2.5H10.214C5.95366 2.5 2.5 5.95366 2.5 10.214V29.786C2.5 34.0463 5.95366 37.5 10.214 37.5H29.786C34.0463 37.5 37.5 34.0463 37.5 29.786V10.214C37.5 5.95366 34.0463 2.5 29.786 2.5ZM10.214 0C4.57295 0 0 4.57295 0 10.214V29.786C0 35.4271 4.57295 40 10.214 40H29.786C35.4271 40 40 35.4271 40 29.786V10.214C40 4.57295 35.4271 0 29.786 0H10.214Z" fill="#0052CD"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"id": "ethora-core",
|
||||
"name": "Ethora Core",
|
||||
"version": "2610",
|
||||
"description": "Self-hosted chat and messaging server: API, web chat and admin panel, XMPP server (ejabberd) and file storage, with SDKs for web, iOS, Android and React Native.",
|
||||
"logo": "ethora.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/dappros/ethora-install",
|
||||
"website": "https://ethora.com/",
|
||||
"docs": "https://github.com/dappros/ethora-install/tree/main/deploy/compose"
|
||||
},
|
||||
"tags": [
|
||||
"chat",
|
||||
"self-hosted"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
feedbacks:
|
||||
image: softinator/feedbacks@sha256:f7d759e671b48906a4aa5deff92b4644a73a6a4239f32b4cebba5cfcef8cc97b
|
||||
restart: unless-stopped
|
||||
init: true
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp:size=128m,mode=1777
|
||||
cap_drop: [ALL]
|
||||
security_opt: [no-new-privileges:true]
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
PORT: 3000
|
||||
APP_ORIGIN: ${APP_ORIGIN:?required}
|
||||
DATABASE_URL: postgresql://feedbacks:${POSTGRES_PASSWORD:?required}@postgres:5432/feedbacks
|
||||
DATABASE_POOL_MAX: ${DATABASE_POOL_MAX:-10}
|
||||
TRUST_PROXY_HOPS: ${TRUST_PROXY_HOPS:-1}
|
||||
ASSET_DRIVER: s3
|
||||
ORGANIZATION_ID: ${ORGANIZATION_ID:?required}
|
||||
# Defaults to the bundled SeaweedFS S3; override all S3_* to use external storage.
|
||||
S3_ENDPOINT: ${S3_ENDPOINT:-https://s3:8333}
|
||||
S3_REGION: ${S3_REGION:-us-east-1}
|
||||
S3_BUCKET: ${S3_BUCKET:-feedbacks}
|
||||
S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:?required}
|
||||
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:?required}
|
||||
# Feedbacks requires an HTTPS S3 endpoint in production; trust the bundled SeaweedFS self-signed cert.
|
||||
NODE_EXTRA_CA_CERTS: /s3-config/public.crt
|
||||
TURNSTILE_SITE_KEY: ${TURNSTILE_SITE_KEY:-}
|
||||
TURNSTILE_SECRET_KEY: ${TURNSTILE_SECRET_KEY:-}
|
||||
GITHUB_APPS_JSON: ${GITHUB_APPS_JSON:-}
|
||||
GITHUB_APP_ID: ${GITHUB_APP_ID:-}
|
||||
GITHUB_APP_SLUG: ${GITHUB_APP_SLUG:-}
|
||||
GITHUB_APP_PRIVATE_KEY_BASE64: ${GITHUB_APP_PRIVATE_KEY_BASE64:-}
|
||||
volumes:
|
||||
- feedbacks-s3-config:/s3-config:ro
|
||||
expose: ["3000"]
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
s3-bucket:
|
||||
condition: service_completed_successfully
|
||||
stop_grace_period: 15s
|
||||
postgres:
|
||||
image: postgres:17-bookworm
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: feedbacks
|
||||
POSTGRES_DB: feedbacks
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?required}
|
||||
volumes: ["feedbacks-postgres:/var/lib/postgresql/data"]
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U feedbacks -d feedbacks"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
s3-init:
|
||||
image: alpine:3.22
|
||||
restart: "no"
|
||||
environment:
|
||||
S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:?required}
|
||||
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:?required}
|
||||
volumes:
|
||||
- feedbacks-s3-config:/config
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
set -e
|
||||
if [ ! -s /config/public.crt ]; then
|
||||
apk add --no-cache openssl >/dev/null
|
||||
openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \
|
||||
-subj "/CN=s3" -addext "subjectAltName=DNS:s3" \
|
||||
-addext "basicConstraints=critical,CA:TRUE" \
|
||||
-keyout /config/private.key -out /config/public.crt
|
||||
fi
|
||||
printf '{"identities":[{"name":"feedbacks","credentials":[{"accessKey":"%s","secretKey":"%s"}],"actions":["Admin","Read","Write","List","Tagging"]}]}' \
|
||||
"$$S3_ACCESS_KEY_ID" "$$S3_SECRET_ACCESS_KEY" > /config/s3.json
|
||||
chmod 644 /config/*
|
||||
s3:
|
||||
image: chrislusf/seaweedfs:4.48
|
||||
restart: unless-stopped
|
||||
command: server -dir=/data -s3 -s3.port=8333 -s3.config=/config/s3.json -s3.cert.file=/config/public.crt -s3.key.file=/config/private.key
|
||||
volumes:
|
||||
- feedbacks-s3:/data
|
||||
- feedbacks-s3-config:/config:ro
|
||||
depends_on:
|
||||
s3-init:
|
||||
condition: service_completed_successfully
|
||||
s3-bucket:
|
||||
image: alpine:3.22
|
||||
restart: "no"
|
||||
depends_on:
|
||||
- s3
|
||||
environment:
|
||||
S3_BUCKET: ${S3_BUCKET:-feedbacks}
|
||||
S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:?required}
|
||||
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:?required}
|
||||
volumes:
|
||||
- feedbacks-s3-config:/config:ro
|
||||
entrypoint: ["/bin/sh", "-c"]
|
||||
command:
|
||||
- |
|
||||
set -e
|
||||
apk add --no-cache curl >/dev/null
|
||||
for i in $$(seq 1 60); do
|
||||
code=$$(curl -s -o /dev/null -w '%{http_code}' --cacert /config/public.crt \
|
||||
--aws-sigv4 "aws:amz:us-east-1:s3" --user "$$S3_ACCESS_KEY_ID:$$S3_SECRET_ACCESS_KEY" \
|
||||
-X PUT "https://s3:8333/$$S3_BUCKET") || code=000
|
||||
case "$$code" in 200|409) echo "bucket ready ($$code)"; exit 0;; esac
|
||||
sleep 2
|
||||
done
|
||||
echo "could not create bucket (last HTTP $$code)"; exit 1
|
||||
volumes:
|
||||
feedbacks-postgres:
|
||||
feedbacks-s3:
|
||||
feedbacks-s3-config:
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"id": "feedbacks",
|
||||
"name": "Feedbacks",
|
||||
"version": "0.2.1",
|
||||
"description": "Self-hosted website feedback, browser evidence and MCP handoff for coding assistants. Ships with private SeaweedFS S3 storage; external S3 is optional.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/Softinator-TechLabs/feedbacks-oss",
|
||||
"website": "https://feedbacks.softinator.ai",
|
||||
"docs": "https://feedbacks.softinator.ai/docs/guide/self-host"
|
||||
},
|
||||
"tags": [
|
||||
"collaboration",
|
||||
"developer-tools"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
postgres_password = "${hash:64}"
|
||||
organization_id = "${uuid}"
|
||||
s3_access_key = "${hash:20}"
|
||||
s3_secret_key = "${hash:40}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"APP_ORIGIN=https://${main_domain}",
|
||||
"POSTGRES_PASSWORD=${postgres_password}",
|
||||
"ORGANIZATION_ID=${organization_id}",
|
||||
"TRUST_PROXY_HOPS=1",
|
||||
# Bundled SeaweedFS S3 (HTTPS with a self-signed cert). To use external S3 instead,
|
||||
# add S3_ENDPOINT, S3_REGION and S3_BUCKET and replace the keys below.
|
||||
"S3_ACCESS_KEY_ID=${s3_access_key}",
|
||||
"S3_SECRET_ACCESS_KEY=${s3_secret_key}",
|
||||
]
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "feedbacks"
|
||||
port = 3000
|
||||
host = "${main_domain}"
|
||||
path = "/"
|
||||
@@ -2,40 +2,54 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
gitea:
|
||||
image: docker.gitea.com/gitea:1.24.4
|
||||
image: docker.gitea.com/gitea:1.27.3
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- USER_UID=1000
|
||||
- USER_GID=1000
|
||||
- USER_UID=${USER_UID:-1000}
|
||||
- USER_GID=${USER_GID:-1000}
|
||||
- GITEA__database__DB_TYPE=postgres
|
||||
- GITEA__database__HOST=postgres:5432
|
||||
- GITEA__database__NAME=gitea
|
||||
- GITEA__database__USER=gitea
|
||||
- GITEA__database__PASSWD=${GITEA_DB_PASSWORD:-gitea}
|
||||
- GITEA__database__NAME=${POSTGRES_DB:-gitea}
|
||||
- GITEA__database__USER=${POSTGRES_USER}
|
||||
- GITEA__database__PASSWD=${POSTGRES_PASSWORD}
|
||||
- GITEA__server__DOMAIN=${GITEA_DOMAIN}
|
||||
- GITEA__server__SSH_DOMAIN=${GITEA_SSH_DOMAIN:-${GITEA_DOMAIN}}
|
||||
- GITEA__server__ROOT_URL=https://${GITEA_DOMAIN}/
|
||||
- GITEA__server__LFS_START_SERVER=true
|
||||
- GITEA__service__DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-false}
|
||||
- GITEA__service__REQUIRE_SIGNIN_VIEW=${REQUIRE_SIGNIN_VIEW:-false}
|
||||
volumes:
|
||||
- gitea-data:/data
|
||||
expose:
|
||||
- "3000"
|
||||
- "22"
|
||||
depends_on:
|
||||
- postgres
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/"]
|
||||
test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/api/healthz"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 20s
|
||||
|
||||
postgres:
|
||||
image: docker.io/library/postgres:14
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- POSTGRES_USER=gitea
|
||||
- POSTGRES_PASSWORD=${GITEA_DB_PASSWORD:-gitea}
|
||||
- POSTGRES_DB=gitea
|
||||
- POSTGRES_USER=${POSTGRES_USER}
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
|
||||
- POSTGRES_DB=${POSTGRES_DB:-gitea}
|
||||
volumes:
|
||||
- pg-data:/var/lib/postgresql/data
|
||||
expose:
|
||||
- "5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB:-gitea}"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
gitea-data: {}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "gitea-postgres",
|
||||
"name": "Gitea (PostgreSQL)",
|
||||
"version": "1.24.4",
|
||||
"version": "1.27.3",
|
||||
"description": "Gitea bundled with PostgreSQL.",
|
||||
"logo": "gitea.png",
|
||||
"links": {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
db_password = "${password:24}"
|
||||
db_user = "${username}"
|
||||
|
||||
[config]
|
||||
mounts = []
|
||||
@@ -12,9 +13,14 @@ host = "${main_domain}"
|
||||
[config.env]
|
||||
USER_UID = "1000"
|
||||
USER_GID = "1000"
|
||||
GITEA__database__DB_TYPE = "postgres"
|
||||
GITEA__database__HOST = "postgres:5432"
|
||||
GITEA__database__NAME = "gitea"
|
||||
GITEA__database__USER = "gitea"
|
||||
GITEA__database__PASSWD = "${db_password}"
|
||||
GITEA_DB_PASSWORD = "${db_password}"
|
||||
GITEA_DOMAIN = "${main_domain}"
|
||||
GITEA_SSH_DOMAIN = "${main_domain}"
|
||||
POSTGRES_USER = "${db_user}"
|
||||
POSTGRES_PASSWORD = "${db_password}"
|
||||
POSTGRES_DB = "gitea"
|
||||
|
||||
# Security & Privacy Settings (default: false):
|
||||
# Set to "true" to disable public user registration (recommended after creating the first admin user)
|
||||
DISABLE_REGISTRATION = "false"
|
||||
# Set to "true" to require sign-in to view repositories and explore pages (makes the instance private)
|
||||
REQUIRE_SIGNIN_VIEW = "false"
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
graylog:
|
||||
image: graylog/graylog:7.1.9
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
mongodb:
|
||||
condition: service_healthy
|
||||
opensearch:
|
||||
condition: service_healthy
|
||||
entrypoint:
|
||||
- /usr/bin/tini
|
||||
- --
|
||||
- /bin/bash
|
||||
- -ec
|
||||
- |
|
||||
export GRAYLOG_ROOT_PASSWORD_SHA2="$$(printf '%s' "$$GRAYLOG_ADMIN_PASSWORD" | sha256sum | cut -d ' ' -f 1)"
|
||||
unset GRAYLOG_ADMIN_PASSWORD
|
||||
exec /docker-entrypoint.sh
|
||||
environment:
|
||||
GRAYLOG_ADMIN_PASSWORD: ${GRAYLOG_ADMIN_PASSWORD:?Set the admin password}
|
||||
GRAYLOG_PASSWORD_SECRET: ${GRAYLOG_PASSWORD_SECRET:?Set the password secret}
|
||||
GRAYLOG_HTTP_BIND_ADDRESS: 0.0.0.0:9000
|
||||
GRAYLOG_HTTP_EXTERNAL_URI: ${GRAYLOG_HTTP_EXTERNAL_URI:?Set the public URL including its trailing slash}
|
||||
GRAYLOG_MONGODB_URI: mongodb://mongodb:27017/graylog
|
||||
GRAYLOG_ELASTICSEARCH_HOSTS: http://opensearch:9200
|
||||
GRAYLOG_NODE_ID_FILE: /usr/share/graylog/data/data/node-id
|
||||
GRAYLOG_SERVER_JAVA_OPTS: -Xms512m -Xmx512m
|
||||
expose:
|
||||
- "9000"
|
||||
- "12201/tcp"
|
||||
- "12201/udp"
|
||||
volumes:
|
||||
- graylog-data:/usr/share/graylog/data
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:9000/api/system/lbstatus"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 120s
|
||||
|
||||
mongodb:
|
||||
image: mongo:8.0.29
|
||||
restart: unless-stopped
|
||||
command: ["mongod", "--wiredTigerCacheSizeGB", "0.25"]
|
||||
volumes:
|
||||
- mongodb-data:/data/db
|
||||
- mongodb-config:/data/configdb
|
||||
healthcheck:
|
||||
test: ["CMD", "mongosh", "--quiet", "--eval", "quit(db.adminCommand('ping').ok ? 0 : 1)"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
|
||||
opensearch:
|
||||
image: opensearchproject/opensearch:2.19.5
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
discovery.type: single-node
|
||||
action.auto_create_index: "false"
|
||||
bootstrap.memory_lock: "true"
|
||||
OPENSEARCH_JAVA_OPTS: -Xms512m -Xmx512m
|
||||
DISABLE_INSTALL_DEMO_CONFIG: "true"
|
||||
DISABLE_SECURITY_PLUGIN: "true"
|
||||
ulimits:
|
||||
memlock:
|
||||
soft: -1
|
||||
hard: -1
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
volumes:
|
||||
- opensearch-data:/usr/share/opensearch/data
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:9200/_cluster/health?wait_for_status=yellow&timeout=5s"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
|
||||
volumes:
|
||||
graylog-data:
|
||||
mongodb-data:
|
||||
mongodb-config:
|
||||
opensearch-data:
|
||||
|
After Width: | Height: | Size: 702 B |
@@ -0,0 +1,37 @@
|
||||
# Graylog
|
||||
|
||||
This template runs Graylog Open with MongoDB and self-managed OpenSearch. It uses the normal Graylog login without Data Node's preflight setup.
|
||||
|
||||
## Before deploying
|
||||
|
||||
- Use an x86-64 server with AVX support for MongoDB.
|
||||
- Allow at least 3 GiB of available RAM for a small installation. Graylog and OpenSearch each start with a 512 MiB Java heap. Increase their heap settings and MongoDB's cache for larger workloads.
|
||||
- Set `vm.max_map_count` to at least `262144` on the Docker host. For example, run `sudo sysctl -w vm.max_map_count=262144` and persist the setting in `/etc/sysctl.d/99-graylog.conf`.
|
||||
- Keep Dokploy's isolated deployment enabled. MongoDB and OpenSearch have no authentication and must stay on the private application network. Do not publish their ports.
|
||||
|
||||
## Login
|
||||
|
||||
Open the domain assigned by Dokploy. Use username `admin` and the generated `GRAYLOG_ADMIN_PASSWORD` from the service's Environment tab. The startup command hashes this password for Graylog.
|
||||
|
||||
When enabling HTTPS or changing the domain in Dokploy, update `GRAYLOG_HTTP_EXTERNAL_URI` to the matching URL, including the trailing slash, and redeploy. Enable HTTPS before sending credentials over an untrusted network.
|
||||
|
||||
Keep `GRAYLOG_PASSWORD_SECRET` unchanged after deployment. Graylog uses it to encrypt stored credentials.
|
||||
|
||||
## Receive logs
|
||||
|
||||
Create an input under **System > Inputs**. For GELF, select **GELF UDP** or **GELF TCP**, bind to `0.0.0.0`, and use port `12201`.
|
||||
|
||||
The template does not publish host ports. To accept logs from outside the application network, add the required port mapping to the `graylog` service in Dokploy's Compose editor and redeploy. For GELF UDP:
|
||||
|
||||
```yaml
|
||||
ports:
|
||||
- "12201:12201/udp"
|
||||
```
|
||||
|
||||
For GELF TCP, use `12201:12201/tcp`. Choose an unused host port and restrict access to trusted log senders in your firewall. Creating an input and publishing its port are both required. HTTP domain routing does not forward TCP or UDP inputs.
|
||||
|
||||
## Data and upgrades
|
||||
|
||||
Named volumes store MongoDB configuration, indexed logs, Graylog's node ID, and its message journal. Preserve all four volumes when redeploying or upgrading, and back up MongoDB and OpenSearch together with Graylog's configuration.
|
||||
|
||||
Check the [Graylog compatibility matrix](https://go2docs.graylog.org/current/downloading_and_installing_graylog/compatibility_matrix.htm) before changing image versions. OpenSearch 3.x is not supported by this Graylog version.
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "graylog",
|
||||
"name": "Graylog",
|
||||
"version": "7.1.9",
|
||||
"description": "Graylog Open collects, indexes, and searches logs, with dashboards and alerts. Includes MongoDB and OpenSearch.",
|
||||
"logo": "graylog.png",
|
||||
"links": {
|
||||
"github": "https://github.com/Graylog2/graylog2-server",
|
||||
"website": "https://graylog.org/",
|
||||
"docs": "https://go2docs.graylog.org/"
|
||||
},
|
||||
"tags": ["monitoring", "logging"]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
admin_password = "${password:32}"
|
||||
password_secret = "${password:96}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"GRAYLOG_ADMIN_PASSWORD=${admin_password}",
|
||||
"GRAYLOG_PASSWORD_SECRET=${password_secret}",
|
||||
"GRAYLOG_HTTP_EXTERNAL_URI=http://${main_domain}/"
|
||||
]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "graylog"
|
||||
port = 9000
|
||||
host = "${main_domain}"
|
||||
@@ -0,0 +1,45 @@
|
||||
# Template validation
|
||||
|
||||
Tested on 2026-09-06 using Dokploy on Linux x86-64, Docker Engine 29.1.3, and the exact image tags in this template.
|
||||
|
||||
## Checks performed
|
||||
|
||||
- Imported the Base64 payload containing `docker-compose.yml` and `template.toml` through Dokploy's `compose.import` API.
|
||||
- Enabled isolated deployment and deployed through Dokploy.
|
||||
- Validated Compose interpolation and syntax with `docker-compose config --quiet`.
|
||||
- Confirmed all three containers became healthy.
|
||||
- Requested the assigned domain through Traefik and received HTTP 200.
|
||||
- Confirmed authenticated API access through Traefik returned HTTP 200 and unauthenticated access to the protected API returned HTTP 401.
|
||||
- Authenticated to `/api/system` with `admin` and the generated password. The server reported `7.1.9+002c047`.
|
||||
- Created a global GELF UDP input on port 12201 through `/api/system/inputs`.
|
||||
- Sent a GELF message to that input over the private container network and retrieved it through Graylog's authenticated search API.
|
||||
- Stopped and started the stack through Dokploy. The message remained searchable, the input remained configured, and Graylog retained its node ID.
|
||||
- The instance owner tested the web interface and confirmed it works.
|
||||
- Verified OpenSearch's effective `action.auto_create_index` setting is `false`. Ingested and searched a message, manually rotated the default index set from `graylog_0` to `graylog_1`, then ingested and found a second message in the new write index.
|
||||
|
||||
The three containers used approximately 2 GiB of RAM during this small test. This is not a throughput or production sizing benchmark.
|
||||
|
||||
Public TCP/UDP port mappings were not tested. The default template publishes no ingestion ports.
|
||||
|
||||
## Repeat the ingestion check
|
||||
|
||||
1. Deploy the template with isolated deployment enabled.
|
||||
2. Log in with the credentials described in `instructions.md`.
|
||||
3. Create a global GELF UDP input bound to `0.0.0.0:12201`.
|
||||
4. From a sender with access to that input, send a GELF message. For example, using Python and replacing `GRAYLOG_INPUT_HOST` with the reachable input address:
|
||||
|
||||
```python
|
||||
import json
|
||||
import socket
|
||||
|
||||
message = {
|
||||
"version": "1.1",
|
||||
"host": "dokploy-template-test",
|
||||
"short_message": "dokploy-graylog-persistence-test",
|
||||
}
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sender:
|
||||
sender.sendto(json.dumps(message).encode(), ("GRAYLOG_INPUT_HOST", 12201))
|
||||
```
|
||||
|
||||
5. Search for `message:"dokploy-graylog-persistence-test"` in Graylog.
|
||||
6. Stop and start the service through Dokploy, then repeat the search and check the input configuration. Keep the search time range wide enough to include the original message.
|
||||
@@ -0,0 +1,452 @@
|
||||
services:
|
||||
postgres:
|
||||
image: "postgres:16.10-alpine"
|
||||
restart: "unless-stopped"
|
||||
environment:
|
||||
POSTGRES_USER: "grovs"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
||||
POSTGRES_DB: "grovs_production"
|
||||
command:
|
||||
- "postgres"
|
||||
- "-c"
|
||||
- "max_connections=200"
|
||||
volumes:
|
||||
- "pg_data:/var/lib/postgresql/data"
|
||||
healthcheck:
|
||||
test:
|
||||
- "CMD-SHELL"
|
||||
- "pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB"
|
||||
interval: "10s"
|
||||
timeout: "5s"
|
||||
retries: 10
|
||||
redis:
|
||||
image: "redis:7.4.5-alpine"
|
||||
restart: "unless-stopped"
|
||||
command:
|
||||
- "redis-server"
|
||||
- "--appendonly"
|
||||
- "yes"
|
||||
- "--maxmemory-policy"
|
||||
- "noeviction"
|
||||
volumes:
|
||||
- "redis_data:/data"
|
||||
healthcheck:
|
||||
test:
|
||||
- "CMD"
|
||||
- "redis-cli"
|
||||
- "ping"
|
||||
interval: "10s"
|
||||
timeout: "5s"
|
||||
retries: 10
|
||||
clickhouse:
|
||||
image: "clickhouse/clickhouse-server:25.3.6.56"
|
||||
restart: "unless-stopped"
|
||||
environment:
|
||||
CLICKHOUSE_DB: "grovs_production"
|
||||
CLICKHOUSE_USER: "grovs"
|
||||
CLICKHOUSE_PASSWORD: "${CLICKHOUSE_PASSWORD}"
|
||||
volumes:
|
||||
- "clickhouse_data:/var/lib/clickhouse"
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 262144
|
||||
hard: 262144
|
||||
healthcheck:
|
||||
test:
|
||||
- "CMD"
|
||||
- "wget"
|
||||
- "--spider"
|
||||
- "-q"
|
||||
- "http://localhost:8123/ping"
|
||||
interval: "10s"
|
||||
timeout: "5s"
|
||||
retries: 30
|
||||
migrate:
|
||||
image: "ghcr.io/grovs-io/backend:2.3.1"
|
||||
restart: "no"
|
||||
environment:
|
||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: "${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY}"
|
||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: "${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT}"
|
||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: "${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY}"
|
||||
ACTIVE_STORAGE_SERVICE: "local"
|
||||
ADMIN_API_KEY: "${ADMIN_API_KEY}"
|
||||
API_HOST: "api.${SERVER_HOST}"
|
||||
BOOTSTRAP_ADMIN_EMAIL: "${BOOTSTRAP_ADMIN_EMAIL}"
|
||||
BOOTSTRAP_ADMIN_PASSWORD: "${BOOTSTRAP_ADMIN_PASSWORD}"
|
||||
CLICKHOUSE_ANALYTICS_ROLLUPS_READ_ENABLED: "true"
|
||||
CLICKHOUSE_ATTRIBUTION_READ_ENABLED: "true"
|
||||
CLICKHOUSE_DATABASE: "grovs_production"
|
||||
CLICKHOUSE_LINK_DIMENSIONS_READ_ENABLED: "true"
|
||||
CLICKHOUSE_PASSWORD: "${CLICKHOUSE_PASSWORD}"
|
||||
CLICKHOUSE_PRIMARY: "true"
|
||||
CLICKHOUSE_READ_ENABLED: "true"
|
||||
CLICKHOUSE_ROLLUP_FAST_LANE: "true"
|
||||
CLICKHOUSE_URL: "http://grovs:${CLICKHOUSE_PASSWORD}@clickhouse:8123"
|
||||
CLICKHOUSE_WRITE_ENABLED: "true"
|
||||
DASHBOARD_CACHE_TTL_SECONDS: "60"
|
||||
DASHBOARD_HOST: "dashboard.${SERVER_HOST}"
|
||||
DATABASE_URL: "postgres://grovs:${POSTGRES_PASSWORD}@postgres:5432/grovs_production"
|
||||
DEFAULT_LINK_SUBTITLE: "Dynamic links, attributions, and referrals across mobile and web platforms."
|
||||
DEFAULT_LINK_TITLE: "grovs"
|
||||
DEFAULT_LOGO_URL: "https://appssemble-assets.s3.eu-north-1.amazonaws.com/linksquared/logo-square-new.svg"
|
||||
DEFAULT_SOCIAL_PREVIEW_URL: "https://appssemble-assets.s3.eu-north-1.amazonaws.com/linksquared/social-media-placeholder.jpg"
|
||||
DIAGNOSTICS_API_KEY: "${DIAGNOSTICS_API_KEY}"
|
||||
DOMAIN_LIVE: "${DOMAIN_LIVE}"
|
||||
DOMAIN_TEST: "${DOMAIN_TEST}"
|
||||
GO_HOST: "go.${SERVER_HOST}"
|
||||
GROVS_EE: "false"
|
||||
GROVS_SELF_HOSTED: "true"
|
||||
LINKS_PROD_HOST: "links.${DOMAIN_LIVE}"
|
||||
LINKS_TEST_HOST: "links.${DOMAIN_TEST}"
|
||||
MAILER_DELIVERY_METHOD: ""
|
||||
MAILER_FROM: "Grovs <noreply@${SERVER_HOST}>"
|
||||
MCP_CONSENT_URL: "${SERVER_HOST_PROTOCOL}dashboard.${SERVER_HOST}/mcp/authorize"
|
||||
MCP_HOST: "mcp.${SERVER_HOST}"
|
||||
OAUTH_CLIENT_SECRET: "${OAUTH_CLIENT_SECRET}"
|
||||
OAUTH_CLIENT_UID: "${OAUTH_CLIENT_UID}"
|
||||
PG_SHADOW_WRITES: "false"
|
||||
POSTGRES_DB: "grovs_production"
|
||||
POSTGRES_MAX_CONNECTIONS: "200"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
||||
POSTGRES_USER: "grovs"
|
||||
PREVIEW_BASE_URL: "${SERVER_HOST_PROTOCOL}preview.${SERVER_HOST}"
|
||||
PREVIEW_HOST: "preview.${SERVER_HOST}"
|
||||
PUBLIC_GO_PROJECT_IDENTIFIER: "go-self-hosted"
|
||||
RAILS_DB_POOL: "10"
|
||||
RAILS_ENV: "production"
|
||||
RAILS_LOG_TO_STDOUT: "true"
|
||||
RAILS_MAX_THREADS: "5"
|
||||
RAILS_SERVE_STATIC_FILES: "true"
|
||||
REACT_HOST: "dashboard.${SERVER_HOST}"
|
||||
REACT_HOST_PROTOCOL: "${SERVER_HOST_PROTOCOL}"
|
||||
REDIS_URL: "redis://redis:6379/0"
|
||||
REVENUE_READS_FROM_LEDGER: "true"
|
||||
S3_ASSET_PREFIX: "${SERVER_HOST_PROTOCOL}api.${SERVER_HOST}"
|
||||
SDK_HOST: "sdk.${SERVER_HOST}"
|
||||
SECRET_KEY_BASE: "${SECRET_KEY_BASE}"
|
||||
SENT_QUOTAS_WEBHOOK_KEY: "${SENT_QUOTAS_WEBHOOK_KEY}"
|
||||
SERVER_HOST: "${SERVER_HOST}"
|
||||
SERVER_HOST_PROTOCOL: "${SERVER_HOST_PROTOCOL}"
|
||||
SIDEKIQ_EVENTS_CONCURRENCY: "10"
|
||||
SMTP_ADDRESS: "smtp.example.com"
|
||||
SMTP_AUTHENTICATION: "plain"
|
||||
SMTP_DOMAIN: "${SERVER_HOST}"
|
||||
SMTP_ENABLE_STARTTLS_AUTO: "true"
|
||||
SMTP_PASSWORD: ""
|
||||
SMTP_PORT: "587"
|
||||
SMTP_USERNAME: ""
|
||||
WEB_CONCURRENCY: "2"
|
||||
volumes:
|
||||
- "storage:/app/storage"
|
||||
command:
|
||||
- "bash"
|
||||
- "-c"
|
||||
- "bin/rails db:prepare && bin/rails db:seed && bin/rails clickhouse:setup"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: "service_healthy"
|
||||
redis:
|
||||
condition: "service_healthy"
|
||||
clickhouse:
|
||||
condition: "service_healthy"
|
||||
web:
|
||||
image: "ghcr.io/grovs-io/backend:2.3.1"
|
||||
restart: "unless-stopped"
|
||||
environment:
|
||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: "${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY}"
|
||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: "${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT}"
|
||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: "${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY}"
|
||||
ACTIVE_STORAGE_SERVICE: "local"
|
||||
ADMIN_API_KEY: "${ADMIN_API_KEY}"
|
||||
API_HOST: "api.${SERVER_HOST}"
|
||||
BOOTSTRAP_ADMIN_EMAIL: "${BOOTSTRAP_ADMIN_EMAIL}"
|
||||
BOOTSTRAP_ADMIN_PASSWORD: "${BOOTSTRAP_ADMIN_PASSWORD}"
|
||||
CLICKHOUSE_ANALYTICS_ROLLUPS_READ_ENABLED: "true"
|
||||
CLICKHOUSE_ATTRIBUTION_READ_ENABLED: "true"
|
||||
CLICKHOUSE_DATABASE: "grovs_production"
|
||||
CLICKHOUSE_LINK_DIMENSIONS_READ_ENABLED: "true"
|
||||
CLICKHOUSE_PASSWORD: "${CLICKHOUSE_PASSWORD}"
|
||||
CLICKHOUSE_PRIMARY: "true"
|
||||
CLICKHOUSE_READ_ENABLED: "true"
|
||||
CLICKHOUSE_ROLLUP_FAST_LANE: "true"
|
||||
CLICKHOUSE_URL: "http://grovs:${CLICKHOUSE_PASSWORD}@clickhouse:8123"
|
||||
CLICKHOUSE_WRITE_ENABLED: "true"
|
||||
DASHBOARD_CACHE_TTL_SECONDS: "60"
|
||||
DASHBOARD_HOST: "dashboard.${SERVER_HOST}"
|
||||
DATABASE_URL: "postgres://grovs:${POSTGRES_PASSWORD}@postgres:5432/grovs_production"
|
||||
DEFAULT_LINK_SUBTITLE: "Dynamic links, attributions, and referrals across mobile and web platforms."
|
||||
DEFAULT_LINK_TITLE: "grovs"
|
||||
DEFAULT_LOGO_URL: "https://appssemble-assets.s3.eu-north-1.amazonaws.com/linksquared/logo-square-new.svg"
|
||||
DEFAULT_SOCIAL_PREVIEW_URL: "https://appssemble-assets.s3.eu-north-1.amazonaws.com/linksquared/social-media-placeholder.jpg"
|
||||
DIAGNOSTICS_API_KEY: "${DIAGNOSTICS_API_KEY}"
|
||||
DOMAIN_LIVE: "${DOMAIN_LIVE}"
|
||||
DOMAIN_TEST: "${DOMAIN_TEST}"
|
||||
GO_HOST: "go.${SERVER_HOST}"
|
||||
GROVS_EE: "false"
|
||||
GROVS_SELF_HOSTED: "true"
|
||||
LINKS_PROD_HOST: "links.${DOMAIN_LIVE}"
|
||||
LINKS_TEST_HOST: "links.${DOMAIN_TEST}"
|
||||
MAILER_DELIVERY_METHOD: ""
|
||||
MAILER_FROM: "Grovs <noreply@${SERVER_HOST}>"
|
||||
MCP_CONSENT_URL: "${SERVER_HOST_PROTOCOL}dashboard.${SERVER_HOST}/mcp/authorize"
|
||||
MCP_HOST: "mcp.${SERVER_HOST}"
|
||||
OAUTH_CLIENT_SECRET: "${OAUTH_CLIENT_SECRET}"
|
||||
OAUTH_CLIENT_UID: "${OAUTH_CLIENT_UID}"
|
||||
PG_SHADOW_WRITES: "false"
|
||||
POSTGRES_DB: "grovs_production"
|
||||
POSTGRES_MAX_CONNECTIONS: "200"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
||||
POSTGRES_USER: "grovs"
|
||||
PREVIEW_BASE_URL: "${SERVER_HOST_PROTOCOL}preview.${SERVER_HOST}"
|
||||
PREVIEW_HOST: "preview.${SERVER_HOST}"
|
||||
PUBLIC_GO_PROJECT_IDENTIFIER: "go-self-hosted"
|
||||
RAILS_DB_POOL: "10"
|
||||
RAILS_ENV: "production"
|
||||
RAILS_LOG_TO_STDOUT: "true"
|
||||
RAILS_MAX_THREADS: "5"
|
||||
RAILS_SERVE_STATIC_FILES: "true"
|
||||
REACT_HOST: "dashboard.${SERVER_HOST}"
|
||||
REACT_HOST_PROTOCOL: "${SERVER_HOST_PROTOCOL}"
|
||||
REDIS_URL: "redis://redis:6379/0"
|
||||
REVENUE_READS_FROM_LEDGER: "true"
|
||||
S3_ASSET_PREFIX: "${SERVER_HOST_PROTOCOL}api.${SERVER_HOST}"
|
||||
SDK_HOST: "sdk.${SERVER_HOST}"
|
||||
SECRET_KEY_BASE: "${SECRET_KEY_BASE}"
|
||||
SENT_QUOTAS_WEBHOOK_KEY: "${SENT_QUOTAS_WEBHOOK_KEY}"
|
||||
SERVER_HOST: "${SERVER_HOST}"
|
||||
SERVER_HOST_PROTOCOL: "${SERVER_HOST_PROTOCOL}"
|
||||
SIDEKIQ_EVENTS_CONCURRENCY: "10"
|
||||
SMTP_ADDRESS: "smtp.example.com"
|
||||
SMTP_AUTHENTICATION: "plain"
|
||||
SMTP_DOMAIN: "${SERVER_HOST}"
|
||||
SMTP_ENABLE_STARTTLS_AUTO: "true"
|
||||
SMTP_PASSWORD: ""
|
||||
SMTP_PORT: "587"
|
||||
SMTP_USERNAME: ""
|
||||
WEB_CONCURRENCY: "2"
|
||||
volumes:
|
||||
- "storage:/app/storage"
|
||||
command:
|
||||
- "bundle"
|
||||
- "exec"
|
||||
- "puma"
|
||||
- "-b"
|
||||
- "tcp://0.0.0.0:3000"
|
||||
expose:
|
||||
- 3000
|
||||
labels:
|
||||
- "traefik.http.services.${APP_NAME}-links.loadbalancer.server.port=3000"
|
||||
- "traefik.http.routers.${APP_NAME}-links.rule=HostRegexp(`^[a-z0-9-]+\\.${DOMAIN_LIVE}$$`) || HostRegexp(`^[a-z0-9-]+\\.${DOMAIN_TEST}$$`)"
|
||||
- "traefik.http.routers.${APP_NAME}-links.entrypoints=web"
|
||||
- "traefik.http.routers.${APP_NAME}-links.priority=1"
|
||||
- "traefik.http.routers.${APP_NAME}-links.service=${APP_NAME}-links"
|
||||
- "traefik.http.routers.${APP_NAME}-links-secure.rule=HostRegexp(`^[a-z0-9-]+\\.${DOMAIN_LIVE}$$`) || HostRegexp(`^[a-z0-9-]+\\.${DOMAIN_TEST}$$`)"
|
||||
- "traefik.http.routers.${APP_NAME}-links-secure.entrypoints=websecure"
|
||||
- "traefik.http.routers.${APP_NAME}-links-secure.priority=1"
|
||||
- "traefik.http.routers.${APP_NAME}-links-secure.service=${APP_NAME}-links"
|
||||
- "traefik.http.routers.${APP_NAME}-links-secure.tls=true"
|
||||
- "traefik.http.routers.${APP_NAME}-links-secure.tls.certresolver=${GROVS_CERT_RESOLVER:-}"
|
||||
- "traefik.http.routers.${APP_NAME}-links-secure.tls.domains[0].main=${DOMAIN_LIVE}"
|
||||
- "traefik.http.routers.${APP_NAME}-links-secure.tls.domains[0].sans=*.${DOMAIN_LIVE}"
|
||||
- "traefik.http.routers.${APP_NAME}-links-secure.tls.domains[1].main=${DOMAIN_TEST}"
|
||||
- "traefik.http.routers.${APP_NAME}-links-secure.tls.domains[1].sans=*.${DOMAIN_TEST}"
|
||||
depends_on:
|
||||
migrate:
|
||||
condition: "service_completed_successfully"
|
||||
healthcheck:
|
||||
test:
|
||||
- "CMD"
|
||||
- "curl"
|
||||
- "-f"
|
||||
- "http://localhost:3000/up"
|
||||
interval: "10s"
|
||||
timeout: "5s"
|
||||
retries: 30
|
||||
start_period: "60s"
|
||||
worker-1:
|
||||
image: "ghcr.io/grovs-io/backend:2.3.1"
|
||||
restart: "unless-stopped"
|
||||
environment:
|
||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: "${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY}"
|
||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: "${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT}"
|
||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: "${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY}"
|
||||
ACTIVE_STORAGE_SERVICE: "local"
|
||||
ADMIN_API_KEY: "${ADMIN_API_KEY}"
|
||||
API_HOST: "api.${SERVER_HOST}"
|
||||
BOOTSTRAP_ADMIN_EMAIL: "${BOOTSTRAP_ADMIN_EMAIL}"
|
||||
BOOTSTRAP_ADMIN_PASSWORD: "${BOOTSTRAP_ADMIN_PASSWORD}"
|
||||
CLICKHOUSE_ANALYTICS_ROLLUPS_READ_ENABLED: "true"
|
||||
CLICKHOUSE_ATTRIBUTION_READ_ENABLED: "true"
|
||||
CLICKHOUSE_DATABASE: "grovs_production"
|
||||
CLICKHOUSE_LINK_DIMENSIONS_READ_ENABLED: "true"
|
||||
CLICKHOUSE_PASSWORD: "${CLICKHOUSE_PASSWORD}"
|
||||
CLICKHOUSE_PRIMARY: "true"
|
||||
CLICKHOUSE_READ_ENABLED: "true"
|
||||
CLICKHOUSE_ROLLUP_FAST_LANE: "true"
|
||||
CLICKHOUSE_URL: "http://grovs:${CLICKHOUSE_PASSWORD}@clickhouse:8123"
|
||||
CLICKHOUSE_WRITE_ENABLED: "true"
|
||||
DASHBOARD_CACHE_TTL_SECONDS: "60"
|
||||
DASHBOARD_HOST: "dashboard.${SERVER_HOST}"
|
||||
DATABASE_URL: "postgres://grovs:${POSTGRES_PASSWORD}@postgres:5432/grovs_production"
|
||||
DEFAULT_LINK_SUBTITLE: "Dynamic links, attributions, and referrals across mobile and web platforms."
|
||||
DEFAULT_LINK_TITLE: "grovs"
|
||||
DEFAULT_LOGO_URL: "https://appssemble-assets.s3.eu-north-1.amazonaws.com/linksquared/logo-square-new.svg"
|
||||
DEFAULT_SOCIAL_PREVIEW_URL: "https://appssemble-assets.s3.eu-north-1.amazonaws.com/linksquared/social-media-placeholder.jpg"
|
||||
DIAGNOSTICS_API_KEY: "${DIAGNOSTICS_API_KEY}"
|
||||
DOMAIN_LIVE: "${DOMAIN_LIVE}"
|
||||
DOMAIN_TEST: "${DOMAIN_TEST}"
|
||||
GO_HOST: "go.${SERVER_HOST}"
|
||||
GROVS_EE: "false"
|
||||
GROVS_SELF_HOSTED: "true"
|
||||
LINKS_PROD_HOST: "links.${DOMAIN_LIVE}"
|
||||
LINKS_TEST_HOST: "links.${DOMAIN_TEST}"
|
||||
MAILER_DELIVERY_METHOD: ""
|
||||
MAILER_FROM: "Grovs <noreply@${SERVER_HOST}>"
|
||||
MCP_CONSENT_URL: "${SERVER_HOST_PROTOCOL}dashboard.${SERVER_HOST}/mcp/authorize"
|
||||
MCP_HOST: "mcp.${SERVER_HOST}"
|
||||
OAUTH_CLIENT_SECRET: "${OAUTH_CLIENT_SECRET}"
|
||||
OAUTH_CLIENT_UID: "${OAUTH_CLIENT_UID}"
|
||||
PG_SHADOW_WRITES: "false"
|
||||
POSTGRES_DB: "grovs_production"
|
||||
POSTGRES_MAX_CONNECTIONS: "200"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
||||
POSTGRES_USER: "grovs"
|
||||
PREVIEW_BASE_URL: "${SERVER_HOST_PROTOCOL}preview.${SERVER_HOST}"
|
||||
PREVIEW_HOST: "preview.${SERVER_HOST}"
|
||||
PUBLIC_GO_PROJECT_IDENTIFIER: "go-self-hosted"
|
||||
RAILS_DB_POOL: "10"
|
||||
RAILS_ENV: "production"
|
||||
RAILS_LOG_TO_STDOUT: "true"
|
||||
RAILS_MAX_THREADS: "5"
|
||||
RAILS_SERVE_STATIC_FILES: "true"
|
||||
REACT_HOST: "dashboard.${SERVER_HOST}"
|
||||
REACT_HOST_PROTOCOL: "${SERVER_HOST_PROTOCOL}"
|
||||
REDIS_URL: "redis://redis:6379/0"
|
||||
REVENUE_READS_FROM_LEDGER: "true"
|
||||
S3_ASSET_PREFIX: "${SERVER_HOST_PROTOCOL}api.${SERVER_HOST}"
|
||||
SDK_HOST: "sdk.${SERVER_HOST}"
|
||||
SECRET_KEY_BASE: "${SECRET_KEY_BASE}"
|
||||
SENT_QUOTAS_WEBHOOK_KEY: "${SENT_QUOTAS_WEBHOOK_KEY}"
|
||||
SERVER_HOST: "${SERVER_HOST}"
|
||||
SERVER_HOST_PROTOCOL: "${SERVER_HOST_PROTOCOL}"
|
||||
SIDEKIQ_EVENTS_CONCURRENCY: "10"
|
||||
SMTP_ADDRESS: "smtp.example.com"
|
||||
SMTP_AUTHENTICATION: "plain"
|
||||
SMTP_DOMAIN: "${SERVER_HOST}"
|
||||
SMTP_ENABLE_STARTTLS_AUTO: "true"
|
||||
SMTP_PASSWORD: ""
|
||||
SMTP_PORT: "587"
|
||||
SMTP_USERNAME: ""
|
||||
WEB_CONCURRENCY: "2"
|
||||
volumes:
|
||||
- "storage:/app/storage"
|
||||
command:
|
||||
- "bash"
|
||||
- "-c"
|
||||
- "bundle exec sidekiq -C config/sidekiq_scheduler.yml & bundle exec sidekiq -C config/sidekiq_worker.yml & bundle exec sidekiq -C config/sidekiq_batch.yml & wait -n"
|
||||
depends_on:
|
||||
web:
|
||||
condition: "service_healthy"
|
||||
worker-2:
|
||||
image: "ghcr.io/grovs-io/backend:2.3.1"
|
||||
restart: "unless-stopped"
|
||||
environment:
|
||||
ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY: "${ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY}"
|
||||
ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT: "${ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT}"
|
||||
ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY: "${ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY}"
|
||||
ACTIVE_STORAGE_SERVICE: "local"
|
||||
ADMIN_API_KEY: "${ADMIN_API_KEY}"
|
||||
API_HOST: "api.${SERVER_HOST}"
|
||||
BOOTSTRAP_ADMIN_EMAIL: "${BOOTSTRAP_ADMIN_EMAIL}"
|
||||
BOOTSTRAP_ADMIN_PASSWORD: "${BOOTSTRAP_ADMIN_PASSWORD}"
|
||||
CLICKHOUSE_ANALYTICS_ROLLUPS_READ_ENABLED: "true"
|
||||
CLICKHOUSE_ATTRIBUTION_READ_ENABLED: "true"
|
||||
CLICKHOUSE_DATABASE: "grovs_production"
|
||||
CLICKHOUSE_LINK_DIMENSIONS_READ_ENABLED: "true"
|
||||
CLICKHOUSE_PASSWORD: "${CLICKHOUSE_PASSWORD}"
|
||||
CLICKHOUSE_PRIMARY: "true"
|
||||
CLICKHOUSE_READ_ENABLED: "true"
|
||||
CLICKHOUSE_ROLLUP_FAST_LANE: "true"
|
||||
CLICKHOUSE_URL: "http://grovs:${CLICKHOUSE_PASSWORD}@clickhouse:8123"
|
||||
CLICKHOUSE_WRITE_ENABLED: "true"
|
||||
DASHBOARD_CACHE_TTL_SECONDS: "60"
|
||||
DASHBOARD_HOST: "dashboard.${SERVER_HOST}"
|
||||
DATABASE_URL: "postgres://grovs:${POSTGRES_PASSWORD}@postgres:5432/grovs_production"
|
||||
DEFAULT_LINK_SUBTITLE: "Dynamic links, attributions, and referrals across mobile and web platforms."
|
||||
DEFAULT_LINK_TITLE: "grovs"
|
||||
DEFAULT_LOGO_URL: "https://appssemble-assets.s3.eu-north-1.amazonaws.com/linksquared/logo-square-new.svg"
|
||||
DEFAULT_SOCIAL_PREVIEW_URL: "https://appssemble-assets.s3.eu-north-1.amazonaws.com/linksquared/social-media-placeholder.jpg"
|
||||
DIAGNOSTICS_API_KEY: "${DIAGNOSTICS_API_KEY}"
|
||||
DOMAIN_LIVE: "${DOMAIN_LIVE}"
|
||||
DOMAIN_TEST: "${DOMAIN_TEST}"
|
||||
GO_HOST: "go.${SERVER_HOST}"
|
||||
GROVS_EE: "false"
|
||||
GROVS_SELF_HOSTED: "true"
|
||||
LINKS_PROD_HOST: "links.${DOMAIN_LIVE}"
|
||||
LINKS_TEST_HOST: "links.${DOMAIN_TEST}"
|
||||
MAILER_DELIVERY_METHOD: ""
|
||||
MAILER_FROM: "Grovs <noreply@${SERVER_HOST}>"
|
||||
MCP_CONSENT_URL: "${SERVER_HOST_PROTOCOL}dashboard.${SERVER_HOST}/mcp/authorize"
|
||||
MCP_HOST: "mcp.${SERVER_HOST}"
|
||||
OAUTH_CLIENT_SECRET: "${OAUTH_CLIENT_SECRET}"
|
||||
OAUTH_CLIENT_UID: "${OAUTH_CLIENT_UID}"
|
||||
PG_SHADOW_WRITES: "false"
|
||||
POSTGRES_DB: "grovs_production"
|
||||
POSTGRES_MAX_CONNECTIONS: "200"
|
||||
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
|
||||
POSTGRES_USER: "grovs"
|
||||
PREVIEW_BASE_URL: "${SERVER_HOST_PROTOCOL}preview.${SERVER_HOST}"
|
||||
PREVIEW_HOST: "preview.${SERVER_HOST}"
|
||||
PUBLIC_GO_PROJECT_IDENTIFIER: "go-self-hosted"
|
||||
RAILS_DB_POOL: "10"
|
||||
RAILS_ENV: "production"
|
||||
RAILS_LOG_TO_STDOUT: "true"
|
||||
RAILS_MAX_THREADS: "5"
|
||||
RAILS_SERVE_STATIC_FILES: "true"
|
||||
REACT_HOST: "dashboard.${SERVER_HOST}"
|
||||
REACT_HOST_PROTOCOL: "${SERVER_HOST_PROTOCOL}"
|
||||
REDIS_URL: "redis://redis:6379/0"
|
||||
REVENUE_READS_FROM_LEDGER: "true"
|
||||
S3_ASSET_PREFIX: "${SERVER_HOST_PROTOCOL}api.${SERVER_HOST}"
|
||||
SDK_HOST: "sdk.${SERVER_HOST}"
|
||||
SECRET_KEY_BASE: "${SECRET_KEY_BASE}"
|
||||
SENT_QUOTAS_WEBHOOK_KEY: "${SENT_QUOTAS_WEBHOOK_KEY}"
|
||||
SERVER_HOST: "${SERVER_HOST}"
|
||||
SERVER_HOST_PROTOCOL: "${SERVER_HOST_PROTOCOL}"
|
||||
SIDEKIQ_EVENTS_CONCURRENCY: "10"
|
||||
SMTP_ADDRESS: "smtp.example.com"
|
||||
SMTP_AUTHENTICATION: "plain"
|
||||
SMTP_DOMAIN: "${SERVER_HOST}"
|
||||
SMTP_ENABLE_STARTTLS_AUTO: "true"
|
||||
SMTP_PASSWORD: ""
|
||||
SMTP_PORT: "587"
|
||||
SMTP_USERNAME: ""
|
||||
WEB_CONCURRENCY: "2"
|
||||
volumes:
|
||||
- "storage:/app/storage"
|
||||
command:
|
||||
- "bash"
|
||||
- "-c"
|
||||
- "bundle exec sidekiq -C config/sidekiq_maintenance.yml & bundle exec sidekiq -C config/sidekiq_device_updates.yml & wait -n"
|
||||
depends_on:
|
||||
web:
|
||||
condition: "service_healthy"
|
||||
dashboard:
|
||||
image: "ghcr.io/grovs-io/dashboard:2.3.1"
|
||||
restart: "unless-stopped"
|
||||
expose:
|
||||
- 3000
|
||||
environment:
|
||||
API_URL: "${SERVER_HOST_PROTOCOL}api.${SERVER_HOST}"
|
||||
OAUTH_CLIENT_UID: "${OAUTH_CLIENT_UID}"
|
||||
OAUTH_CLIENT_SECRET: "${OAUTH_CLIENT_SECRET}"
|
||||
HOSTNAME: "0.0.0.0"
|
||||
PORT: "3000"
|
||||
depends_on:
|
||||
web:
|
||||
condition: "service_healthy"
|
||||
volumes:
|
||||
pg_data: {}
|
||||
redis_data: {}
|
||||
clickhouse_data: {}
|
||||
storage: {}
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 5.21739C0 2.33591 2.33591 0 5.21739 0H34.7826C37.6641 0 40 2.33591 40 5.21739V34.7826C40 37.6641 37.6641 40 34.7826 40H5.21739C2.33591 40 0 37.6641 0 34.7826V5.21739Z" fill="#1F1F1F"/>
|
||||
<path d="M28.5735 22.1385C26.9079 21.8068 25.3362 22.1872 24.1183 23.0271C23.2448 23.6266 22.124 23.7422 21.166 23.2796L20.6713 23.0362C19.5442 22.4915 18.8961 21.3503 18.9024 20.127V20.1057C18.9024 20.0783 18.8961 20.0449 18.8961 20.0175C18.8742 18.8337 19.5129 17.7321 20.6087 17.2026L22.8034 16.1345C23.6425 15.7267 24.6287 15.7663 25.4521 16.2015C26.2285 16.6032 27.1459 16.7705 28.1164 16.6032C29.7883 16.3171 31.1314 14.9995 31.4226 13.3745C31.8484 10.943 29.9824 8.81594 27.5842 8.7003C26.1283 8.62727 24.8009 9.42151 23.9775 10.5961C23.6018 11.1317 23.4045 11.6369 23.3262 12.1238C23.1728 13.0062 22.6124 13.767 21.7953 14.1626L18.6425 15.6933C17.8442 16.0797 16.9018 16.0858 16.1097 15.6872C15.1454 15.2064 14.0058 15.0147 12.8098 15.2399C10.7873 15.6142 9.15924 17.2026 8.78354 19.1745C8.19181 22.315 10.6683 25.0659 13.7991 25.0659C14.6382 25.0659 15.4366 24.862 16.1347 24.509C16.9174 24.1134 17.8536 24.1347 18.6394 24.5212L20.2267 25.2942C21.1441 25.7415 21.6826 26.6605 21.7076 27.6586C21.7233 28.2216 21.8328 28.8089 22.0708 29.4175C22.866 31.4077 24.7476 32.8684 26.9361 33.0266C30.6148 33.3035 33.6361 30.1326 32.9442 26.4901C32.5371 24.3326 30.7776 22.5797 28.5735 22.1385Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"id": "grovs",
|
||||
"name": "Grovs",
|
||||
"version": "2.3.1",
|
||||
"description": "Self-hosted deep linking, attribution and analytics for mobile and web apps.",
|
||||
"logo": "logo.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/grovs-io/self-host",
|
||||
"website": "https://www.grovs.io",
|
||||
"docs": "https://github.com/grovs-io/self-host/blob/main/deploy/catalogs/dokploy/README.md"
|
||||
},
|
||||
"tags": [
|
||||
"analytics",
|
||||
"developer-tools"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
postgres_password = "${password:32}"
|
||||
clickhouse_password = "${password:32}"
|
||||
secret_key_base = "${hash:64}"
|
||||
active_record_encryption_primary_key = "${hash:64}"
|
||||
active_record_encryption_deterministic_key = "${hash:64}"
|
||||
active_record_encryption_key_derivation_salt = "${hash:64}"
|
||||
admin_api_key = "${hash:64}"
|
||||
diagnostics_api_key = "${hash:64}"
|
||||
sent_quotas_webhook_key = "${hash:64}"
|
||||
oauth_client_uid = "${hash:48}"
|
||||
oauth_client_secret = "${hash:64}"
|
||||
bootstrap_admin_password = "${password:24}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"SERVER_HOST=${main_domain}",
|
||||
"DOMAIN_LIVE=${main_domain}",
|
||||
"DOMAIN_TEST=test.${main_domain}",
|
||||
"SERVER_HOST_PROTOCOL=http://",
|
||||
"GROVS_CERT_RESOLVER=",
|
||||
"BOOTSTRAP_ADMIN_EMAIL=admin@${main_domain}",
|
||||
"POSTGRES_PASSWORD=${postgres_password}",
|
||||
"CLICKHOUSE_PASSWORD=${clickhouse_password}",
|
||||
"SECRET_KEY_BASE=${secret_key_base}",
|
||||
"ACTIVE_RECORD_ENCRYPTION_PRIMARY_KEY=${active_record_encryption_primary_key}",
|
||||
"ACTIVE_RECORD_ENCRYPTION_DETERMINISTIC_KEY=${active_record_encryption_deterministic_key}",
|
||||
"ACTIVE_RECORD_ENCRYPTION_KEY_DERIVATION_SALT=${active_record_encryption_key_derivation_salt}",
|
||||
"ADMIN_API_KEY=${admin_api_key}",
|
||||
"DIAGNOSTICS_API_KEY=${diagnostics_api_key}",
|
||||
"SENT_QUOTAS_WEBHOOK_KEY=${sent_quotas_webhook_key}",
|
||||
"OAUTH_CLIENT_UID=${oauth_client_uid}",
|
||||
"OAUTH_CLIENT_SECRET=${oauth_client_secret}",
|
||||
"BOOTSTRAP_ADMIN_PASSWORD=${bootstrap_admin_password}",
|
||||
]
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "dashboard"
|
||||
port = 3000
|
||||
host = "dashboard.${main_domain}"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "web"
|
||||
port = 3000
|
||||
host = "api.${main_domain}"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "web"
|
||||
port = 3000
|
||||
host = "sdk.${main_domain}"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "web"
|
||||
port = 3000
|
||||
host = "mcp.${main_domain}"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "web"
|
||||
port = 3000
|
||||
host = "go.${main_domain}"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "web"
|
||||
port = 3000
|
||||
host = "preview.${main_domain}"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "web"
|
||||
port = 3000
|
||||
host = "links.${main_domain}"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "web"
|
||||
port = 3000
|
||||
host = "links.test.${main_domain}"
|
||||
@@ -0,0 +1,49 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
healthchecks:
|
||||
image: healthchecks/healthchecks:v4.4
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
expose:
|
||||
- 8000
|
||||
environment:
|
||||
DEBUG: ${DEBUG}
|
||||
DB: postgres
|
||||
DB_HOST: db
|
||||
DB_PORT: "5432"
|
||||
DB_NAME: ${DB_NAME}
|
||||
DB_USER: ${DB_USER}
|
||||
DB_PASSWORD: ${DB_PASSWORD}
|
||||
SECRET_KEY: ${SECRET_KEY}
|
||||
SITE_ROOT: ${SITE_ROOT}
|
||||
SITE_NAME: ${SITE_NAME}
|
||||
ALLOWED_HOSTS: ${ALLOWED_HOSTS}
|
||||
PING_ENDPOINT: ${PING_ENDPOINT}
|
||||
REGISTRATION_OPEN: ${REGISTRATION_OPEN}
|
||||
DEFAULT_FROM_EMAIL: ${DEFAULT_FROM_EMAIL}
|
||||
EMAIL_HOST: ${EMAIL_HOST}
|
||||
EMAIL_PORT: ${EMAIL_PORT}
|
||||
EMAIL_HOST_USER: ${EMAIL_HOST_USER}
|
||||
EMAIL_HOST_PASSWORD: ${EMAIL_HOST_PASSWORD}
|
||||
EMAIL_USE_TLS: ${EMAIL_USE_TLS}
|
||||
|
||||
db:
|
||||
image: postgres:16-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: ${DB_NAME}
|
||||
POSTGRES_USER: ${DB_USER}
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
volumes:
|
||||
- healthchecks-db:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
healthchecks-db:
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg width="120mm" height="120mm" version="1.1" viewBox="0 0 120 120" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="translate(-27.502 -156.01)">
|
||||
<path d="m38.253 241.39 2.9915-19.772 23.014-0.13391 11.092-63.965 18.257-2e-3 7.7596 46.333-13.449 0.0645-9.8934 57.109-18.286-9e-3 -3.23-19.585z" fill="#33333f"/>
|
||||
<path d="m94.923 274.5-9.4557-56.483 1.4735-8.4919 16.484 0.0429 2.4469 12.936 30.88 0.0734-2.1276 19.624-16.084 0.0801-5.347 32.207z" fill="#5cb85f"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 484 B |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "healthchecks",
|
||||
"name": "Healthchecks",
|
||||
"version": "v4.4",
|
||||
"description": "Healthchecks is an open-source cron job and background task monitoring service that alerts you when your scheduled jobs fail to check in. Registration is closed by default: create the first admin by running ./manage.py createsuperuser in the healthchecks container terminal.",
|
||||
"logo": "healthchecks.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/healthchecks/healthchecks",
|
||||
"website": "https://healthchecks.io/",
|
||||
"docs": "https://healthchecks.io/docs/self_hosted_docker/"
|
||||
},
|
||||
"tags": ["monitoring", "cron", "alerting"]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
db_password = "${password:32}"
|
||||
secret_key = "${password:64}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"DEBUG=False",
|
||||
"DB_NAME=healthchecks",
|
||||
"DB_USER=healthchecks",
|
||||
"DB_PASSWORD=${db_password}",
|
||||
"SECRET_KEY=${secret_key}",
|
||||
"SITE_ROOT=https://${main_domain}",
|
||||
"SITE_NAME=Healthchecks",
|
||||
"ALLOWED_HOSTS=${main_domain}",
|
||||
"PING_ENDPOINT=https://${main_domain}/ping/",
|
||||
"REGISTRATION_OPEN=False",
|
||||
"DEFAULT_FROM_EMAIL=healthchecks@${main_domain}",
|
||||
"EMAIL_HOST=",
|
||||
"EMAIL_PORT=587",
|
||||
"EMAIL_HOST_USER=",
|
||||
"EMAIL_HOST_PASSWORD=",
|
||||
"EMAIL_USE_TLS=True",
|
||||
]
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "healthchecks"
|
||||
port = 8000
|
||||
host = "${main_domain}"
|
||||
@@ -0,0 +1,117 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
hyperdx:
|
||||
image: hyperdx/hyperdx:2.38.0
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
mongodb:
|
||||
condition: service_healthy
|
||||
clickhouse:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
FRONTEND_URL: ${HYPERDX_URL:?Set the public HyperDX URL}
|
||||
HYPERDX_APP_URL: ${HYPERDX_URL:?Set the public HyperDX URL}
|
||||
HYPERDX_APP_PORT: "8080"
|
||||
HYPERDX_API_PORT: "8000"
|
||||
SERVER_URL: http://127.0.0.1:8000
|
||||
OPAMP_PORT: "4320"
|
||||
MONGO_URI: mongodb://mongodb:27017/hyperdx
|
||||
EXPRESS_SESSION_SECRET: ${SESSION_SECRET:?Set the session secret}
|
||||
HYPERDX_LOG_LEVEL: info
|
||||
USAGE_STATS_ENABLED: "false"
|
||||
NEXT_TELEMETRY_DISABLED: "1"
|
||||
DEFAULT_CONNECTIONS: '[{"name":"Local
|
||||
ClickHouse","host":"http://clickhouse:8123","username":"hyperdx","password":"${CLICKHOUSE_PASSWORD}"}]'
|
||||
DEFAULT_SOURCES: '[{"from":{"databaseName":"default","tableName":"otel_logs"},"kind":"log","timestampValueExpression":"Timestamp","name":"Logs","displayedTimestampValueExpression":"Timestamp","implicitColumnExpression":"Body","serviceNameExpression":"ServiceName","bodyExpression":"Body","eventAttributesExpression":"LogAttributes","resourceAttributesExpression":"ResourceAttributes","defaultTableSelectExpression":"Timestamp,ServiceName,SeverityText,Body","severityTextExpression":"SeverityText","traceIdExpression":"TraceId","spanIdExpression":"SpanId","metadataMaterializedViews":{"keyRollupTable":"otel_logs_key_rollup_15m","kvRollupTable":"otel_logs_kv_rollup_15m","granularity":"15
|
||||
minute"},"connection":"Local
|
||||
ClickHouse","traceSourceId":"Traces","sessionSourceId":"Sessions","metricSourceId":"Metrics"},{"from":{"databaseName":"default","tableName":"otel_traces"},"kind":"trace","timestampValueExpression":"Timestamp","name":"Traces","displayedTimestampValueExpression":"Timestamp","implicitColumnExpression":"SpanName","serviceNameExpression":"ServiceName","eventAttributesExpression":"SpanAttributes","resourceAttributesExpression":"ResourceAttributes","defaultTableSelectExpression":"Timestamp,ServiceName,StatusCode,round(Duration/1e6),SpanName","traceIdExpression":"TraceId","spanIdExpression":"SpanId","durationExpression":"Duration","durationPrecision":9,"parentSpanIdExpression":"ParentSpanId","spanNameExpression":"SpanName","spanKindExpression":"SpanKind","statusCodeExpression":"StatusCode","statusMessageExpression":"StatusMessage","metadataMaterializedViews":{"keyRollupTable":"otel_traces_key_rollup_15m","kvRollupTable":"otel_traces_kv_rollup_15m","granularity":"15
|
||||
minute"},"connection":"Local
|
||||
ClickHouse","logSourceId":"Logs","sessionSourceId":"Sessions","metricSourceId":"Metrics"},{"from":{"databaseName":"default","tableName":""},"kind":"metric","timestampValueExpression":"TimeUnix","name":"Metrics","resourceAttributesExpression":"ResourceAttributes","metricTables":{"gauge":"otel_metrics_gauge","histogram":"otel_metrics_histogram","sum":"otel_metrics_sum","exponential histogram":"otel_metrics_exponential_histogram","_id":"682586a8b1f81924e628e808","id":"682586a8b1f81924e628e808"},"connection":"Local
|
||||
ClickHouse","logSourceId":"Logs","traceSourceId":"Traces","sessionSourceId":"Sessions"},{"from":{"databaseName":"default","tableName":"hyperdx_sessions"},"kind":"session","timestampValueExpression":"TimestampTime","name":"Sessions","displayedTimestampValueExpression":"Timestamp","implicitColumnExpression":"Body","serviceNameExpression":"ServiceName","bodyExpression":"Body","eventAttributesExpression":"LogAttributes","resourceAttributesExpression":"ResourceAttributes","defaultTableSelectExpression":"Timestamp,ServiceName,SeverityText,Body","severityTextExpression":"SeverityText","traceIdExpression":"TraceId","spanIdExpression":"SpanId","connection":"Local
|
||||
ClickHouse","logSourceId":"Logs","traceSourceId":"Traces","metricSourceId":"Metrics"}]'
|
||||
expose:
|
||||
- "8080"
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- node
|
||||
- -e
|
||||
- require('http').get('http://127.0.0.1:8080/api/ready',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
mongodb:
|
||||
image: mongo:8.0.29
|
||||
restart: unless-stopped
|
||||
command:
|
||||
- mongod
|
||||
- --wiredTigerCacheSizeGB
|
||||
- "0.25"
|
||||
volumes:
|
||||
- mongodb-data:/data/db
|
||||
- mongodb-config:/data/configdb
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- mongosh
|
||||
- --quiet
|
||||
- --eval
|
||||
- "quit(db.adminCommand('ping').ok ? 0 : 1)"
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
clickhouse:
|
||||
image: clickhouse/clickhouse-server:26.5.7.64-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
CLICKHOUSE_USER: hyperdx
|
||||
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:?Set the ClickHouse password}
|
||||
CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: "1"
|
||||
ulimits:
|
||||
nofile:
|
||||
soft: 262144
|
||||
hard: 262144
|
||||
volumes:
|
||||
- clickhouse-data:/var/lib/clickhouse
|
||||
- clickhouse-logs:/var/log/clickhouse-server
|
||||
- ../files/clickhouse-config.xml:/etc/clickhouse-server/config.d/hyperdx.xml:ro
|
||||
healthcheck:
|
||||
test:
|
||||
- CMD
|
||||
- wget
|
||||
- --quiet
|
||||
- --spider
|
||||
- http://127.0.0.1:8123/ping
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
otel-collector:
|
||||
image: clickhouse/clickstack-otel-collector:2.38.0
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
clickhouse:
|
||||
condition: service_healthy
|
||||
hyperdx:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
CLICKHOUSE_ENDPOINT: tcp://clickhouse:9000?dial_timeout=10s
|
||||
CLICKHOUSE_USER: hyperdx
|
||||
CLICKHOUSE_PASSWORD: ${CLICKHOUSE_PASSWORD:?Set the ClickHouse password}
|
||||
HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE: default
|
||||
HYPERDX_OTEL_EXPORTER_CREATE_LEGACY_SCHEMA: "true"
|
||||
OPAMP_SERVER_URL: http://hyperdx:4320
|
||||
HYPERDX_LOG_LEVEL: info
|
||||
expose:
|
||||
- "4317"
|
||||
- "4318"
|
||||
volumes:
|
||||
- otel-supervisor:/etc/otel/supervisor-data
|
||||
volumes:
|
||||
mongodb-data: {}
|
||||
mongodb-config: {}
|
||||
clickhouse-data: {}
|
||||
clickhouse-logs: {}
|
||||
otel-supervisor: {}
|
||||
@@ -0,0 +1,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="260" height="260" fill="none" viewBox="0 0 260 260">
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<rect width="260" height="260" fill="#fff" rx="40" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="#25E2A5" d="M242.166 65v130l-112.583 65.001L17 195V65L129.583 0l112.583 65Z" />
|
||||
<path fill="#1E1E1E"
|
||||
d="M157.698 42.893c1.754 1.284 2.573 3.92 1.976 6.361l-15.713 64.274h28.994c1.74 0 3.314 1.302 4.004 3.313.691 2.011.366 4.346-.827 5.941l-69.801 93.342c-1.391 1.86-3.617 2.267-5.371.984-1.753-1.285-2.572-3.921-1.976-6.362l15.713-64.274H85.704c-1.74 0-3.315-1.302-4.005-3.312-.69-2.011-.365-4.346.828-5.942l69.801-93.342c1.39-1.86 3.616-2.267 5.37-.983Z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 741 B |
@@ -0,0 +1,41 @@
|
||||
# HyperDX
|
||||
|
||||
This template deploys HyperDX with MongoDB, ClickHouse, and the ClickStack OpenTelemetry collector. It uses HyperDX's authenticated application image. Logs, traces, metrics, and session replay sources are configured automatically when you create the first account.
|
||||
|
||||
## Before deploying
|
||||
|
||||
Use an x86-64 server with AVX support for MongoDB, at least 2 CPU cores, and 4 GiB of available RAM for testing. Reserve more memory and disk space for production workloads.
|
||||
|
||||
Keep Dokploy's isolated deployment enabled. MongoDB has no authentication and must remain on the private application network. ClickHouse uses a generated password. Neither database needs a public port.
|
||||
|
||||
The ClickHouse configuration starts with a 2 GiB server memory budget and small caches. Adjust `clickhouse-config.xml` in Dokploy's file mounts for your workload and available memory. This setting is ClickHouse's accounting limit, not a hard container RAM limit.
|
||||
|
||||
## First login
|
||||
|
||||
Open the assigned domain and create the first account. HyperDX allows one initial team; invite additional users from that team after setup. Complete this step before sharing the domain.
|
||||
|
||||
When enabling HTTPS or changing the domain in Dokploy, set `HYPERDX_URL` to the matching origin, such as `https://hyperdx.example.com`, and redeploy. Use HTTPS before sending credentials over an untrusted network. The web interface proxies API requests internally, so you do not need to publish API port 8000.
|
||||
|
||||
Keep `SESSION_SECRET` stable across redeployments. If you change `CLICKHOUSE_PASSWORD` after creating your account, also update the saved ClickHouse connection in HyperDX.
|
||||
|
||||
## Send telemetry
|
||||
|
||||
Copy the ingestion API key from HyperDX's team settings. This key is different from the personal API key used to query HyperDX's API.
|
||||
|
||||
For senders on the private application network:
|
||||
|
||||
```text
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
|
||||
OTEL_EXPORTER_OTLP_HEADERS=authorization=<ingestion-api-key>
|
||||
```
|
||||
|
||||
For external senders, add a domain in Dokploy for service `otel-collector`, port `4318`, enable HTTPS, and use that URL as the OTLP endpoint. HTTP/protobuf telemetry uses `/v1/logs`, `/v1/traces`, and `/v1/metrics` beneath that endpoint. The collector requires the ingestion API key after the first team is created.
|
||||
|
||||
For OTLP gRPC, publish an unused host port to collector port `4317` in Dokploy's Compose editor and restrict access to trusted senders. Ordinary HTTP domain routing does not configure gRPC ingestion automatically. The template publishes no host ports by default.
|
||||
|
||||
## Persistence
|
||||
|
||||
MongoDB volumes store accounts, connections, dashboards, and saved searches. ClickHouse volumes store telemetry and server logs. The collector volume retains its supervisor state. Preserve these volumes during redeployments and back up MongoDB and ClickHouse before upgrading.
|
||||
|
||||
Usage reporting and Next.js telemetry are disabled by default. Configure retention in ClickHouse for the volume of telemetry you expect.
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "hyperdx",
|
||||
"name": "HyperDX",
|
||||
"version": "2.38.0",
|
||||
"description": "HyperDX searches and visualizes logs, traces, metrics, and session replays. Includes ClickHouse, MongoDB, and the ClickStack OpenTelemetry collector.",
|
||||
"logo": "hyperdx.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/hyperdxio/hyperdx",
|
||||
"website": "https://hyperdx.io/",
|
||||
"docs": "https://clickhouse.com/docs/use-cases/observability/clickstack"
|
||||
},
|
||||
"tags": ["monitoring", "logging", "observability"]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
session_secret = "${password:64}"
|
||||
clickhouse_password = "${password:32}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"HYPERDX_URL=http://${main_domain}",
|
||||
"SESSION_SECRET=${session_secret}",
|
||||
"CLICKHOUSE_PASSWORD=${clickhouse_password}"
|
||||
]
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "hyperdx"
|
||||
port = 8080
|
||||
host = "${main_domain}"
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "clickhouse-config.xml"
|
||||
content = """
|
||||
<clickhouse>
|
||||
<logger>
|
||||
<level>warning</level>
|
||||
</logger>
|
||||
<custom_settings_prefixes>hyperdx</custom_settings_prefixes>
|
||||
<max_server_memory_usage>2147483648</max_server_memory_usage>
|
||||
<mark_cache_size>134217728</mark_cache_size>
|
||||
<uncompressed_cache_size>67108864</uncompressed_cache_size>
|
||||
</clickhouse>
|
||||
"""
|
||||
@@ -0,0 +1,27 @@
|
||||
# Template validation
|
||||
|
||||
Tested on 2026-09-06 through Dokploy on Linux x86-64 with Docker Engine 29.1.3, using the image versions pinned in this template.
|
||||
|
||||
## Checks performed
|
||||
|
||||
- Imported the Compose and TOML files through Dokploy's Base64 import API with isolated deployment enabled.
|
||||
- Confirmed Dokploy created the ClickHouse configuration file mount and all four services became healthy.
|
||||
- Created the first account and logged in through the assigned domain and Traefik. HyperDX created Logs, Traces, Metrics, and Sessions sources automatically.
|
||||
- Sent OTLP JSON logs, traces, and metrics to the collector over its private network, authenticated with the team's ingestion key.
|
||||
- Confirmed unauthenticated ingestion returned HTTP 401.
|
||||
- Retrieved the test log and trace through HyperDX's authenticated search API.
|
||||
- Stopped and started the stack through Dokploy. Login, sources, and log and trace searches still worked. Confirmed the test metric value remained in ClickHouse.
|
||||
- The instance owner tested the web interface and confirmed it works.
|
||||
- Sent an OTLP exponential histogram, verified the saved Metrics source maps `exponential histogram` to `otel_metrics_exponential_histogram`, and discovered the metric through HyperDX's authenticated ClickHouse query proxy using that mapping.
|
||||
|
||||
The four containers used approximately 0.9 GiB during this small test. This does not replace the upstream recommendation of at least 4 GiB of available memory, and is not a production sizing benchmark.
|
||||
|
||||
Session replay ingestion and public OTLP/gRPC exposure have not been tested.
|
||||
|
||||
## Repeat the ingestion check
|
||||
|
||||
1. Deploy with isolated deployment enabled and create the first account.
|
||||
2. Copy the team ingestion API key and configure your sender as described in `instructions.md`.
|
||||
3. Send an OpenTelemetry log with a unique body and a trace with a unique span name. Send a gauge metric with a known value.
|
||||
4. Find the log and trace in HyperDX, then check the metric.
|
||||
5. Stop and start the stack through Dokploy. Confirm you can log in again and find the same data. Set a search time range that includes the original events.
|
||||
@@ -2,14 +2,14 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
instatic-init:
|
||||
image: ghcr.io/corebunch/instatic:0.0.16
|
||||
image: ghcr.io/corebunch/instatic:0.0.18
|
||||
user: "0:0"
|
||||
entrypoint: ["chown", "-R", "bun:bun", "/app/storage"]
|
||||
volumes:
|
||||
- instatic-storage:/app/storage
|
||||
|
||||
instatic:
|
||||
image: ghcr.io/corebunch/instatic:0.0.16
|
||||
image: ghcr.io/corebunch/instatic:0.0.18
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "instatic",
|
||||
"name": "Instatic",
|
||||
"version": "0.0.16",
|
||||
"version": "0.0.18",
|
||||
"description": "Instatic is a self-hosted, open-source visual CMS combining a canvas editor, content engine, and static site publisher, producing clean semantic HTML with no framework runtime shipped to visitors.",
|
||||
"logo": "instatic.svg",
|
||||
"links": {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
junctio:
|
||||
image: ghcr.io/k2so-dev/junctio:0.2.1
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- 3000
|
||||
environment:
|
||||
JUNCTIO_SECRET: ${JUNCTIO_SECRET}
|
||||
JUNCTIO_BASE_URL: ${JUNCTIO_BASE_URL}
|
||||
JUNCTIO_TRUST_PROXY: ${JUNCTIO_TRUST_PROXY}
|
||||
volumes:
|
||||
- junctio-data:/data
|
||||
- junctio-cache:/cache
|
||||
tmpfs:
|
||||
- /tmp
|
||||
read_only: true
|
||||
|
||||
volumes:
|
||||
junctio-data: {}
|
||||
junctio-cache: {}
|
||||
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"id": "junctio",
|
||||
"name": "Junctio",
|
||||
"version": "0.2.1",
|
||||
"description": "Self-hosted MCP gateway that puts stdio, Docker and remote MCP servers behind one endpoint per client, with API key or OAuth downstream auth and upstream tokens refreshed before they expire.",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/k2so-dev/junctio",
|
||||
"website": "https://junctio.pages.dev",
|
||||
"docs": "https://junctio.pages.dev/docs/"
|
||||
},
|
||||
"tags": [
|
||||
"ai",
|
||||
"mcp",
|
||||
"gateway",
|
||||
"oauth",
|
||||
"developer-tools"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
junctio_secret = "${hash:64}"
|
||||
|
||||
[config]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "junctio"
|
||||
port = 3000
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
JUNCTIO_SECRET = "${junctio_secret}"
|
||||
JUNCTIO_BASE_URL = "https://${main_domain}"
|
||||
JUNCTIO_TRUST_PROXY = "true"
|
||||
@@ -0,0 +1,65 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
keyzori:
|
||||
image: ghcr.io/keyzori/keyzori:93b5d75ac8e5a4cd0740bfe1a46b5663674767fb
|
||||
restart: unless-stopped
|
||||
read_only: true
|
||||
tmpfs:
|
||||
- /tmp:rw,noexec,nosuid,size=16m
|
||||
cap_drop:
|
||||
- ALL
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
env_file:
|
||||
- path: .env
|
||||
required: false
|
||||
environment:
|
||||
KEYZORI_ADMIN_KEY: ${KEYZORI_ADMIN_KEY:?Set KEYZORI_ADMIN_KEY}
|
||||
KEYZORI_DATABASE_URL: postgresql://keyzori:${KEYZORI_POSTGRES_PASSWORD:?Set KEYZORI_POSTGRES_PASSWORD}@postgres:5432/keyzori
|
||||
KEYZORI_REDIS_URL: redis://redis:6379
|
||||
expose:
|
||||
- "6284"
|
||||
stop_grace_period: 30s
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "-T", "5", "-O", "/dev/null", "http://127.0.0.1:6284/ready"]
|
||||
interval: 15s
|
||||
timeout: 6s
|
||||
start_period: 60s
|
||||
retries: 3
|
||||
|
||||
postgres:
|
||||
image: postgres:18.3-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: keyzori
|
||||
POSTGRES_DB: keyzori
|
||||
POSTGRES_PASSWORD: ${KEYZORI_POSTGRES_PASSWORD:?Set KEYZORI_POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U keyzori -d keyzori"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
|
||||
redis:
|
||||
image: redis:8.6.1-alpine
|
||||
restart: unless-stopped
|
||||
command: ["redis-server", "--appendonly", "yes", "--maxmemory", "256mb", "--maxmemory-policy", "noeviction"]
|
||||
volumes:
|
||||
- redis-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
redis-data:
|
||||
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "keyzori",
|
||||
"name": "Keyzori",
|
||||
"version": "93b5d75ac8e5a4cd0740bfe1a46b5663674767fb",
|
||||
"description": "Self-hosted software license server with customer management, device access controls, sessions, and usage metering through an HTTP API and CLI.",
|
||||
"links": {
|
||||
"github": "https://github.com/keyzori/keyzori",
|
||||
"website": "",
|
||||
"docs": "https://github.com/keyzori/keyzori/wiki"
|
||||
},
|
||||
"logo": "keyzori.png",
|
||||
"tags": ["licensing", "security", "developer-tools"]
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
admin_key = "${hash:32}"
|
||||
postgres_password = "${hash:32}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"KEYZORI_ADMIN_KEY=${admin_key}",
|
||||
"KEYZORI_POSTGRES_PASSWORD=${postgres_password}",
|
||||
]
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "keyzori"
|
||||
port = 6284
|
||||
host = "${main_domain}"
|
||||
@@ -0,0 +1,15 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
lfsx:
|
||||
image: ghcr.io/ferrlabs/lfsx:1.14.1
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- LFSX_AUTH=github
|
||||
volumes:
|
||||
- lfsx-data:/var/lib/lfsx
|
||||
expose:
|
||||
- "8080"
|
||||
|
||||
volumes:
|
||||
lfsx-data: {}
|
||||
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<rect width="32" height="32" fill="#1d6b52" />
|
||||
<rect x="3" y="3" width="12" height="12" fill="#f3f2f2" />
|
||||
<rect x="17" y="3" width="12" height="12" fill="#f3f2f2" />
|
||||
<rect x="3" y="17" width="12" height="12" fill="#f3f2f2" />
|
||||
<rect x="17" y="17" width="12" height="12" fill="#7fa896" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 365 B |
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"id": "lfsx",
|
||||
"name": "LFSX",
|
||||
"version": "1.14.1",
|
||||
"description": "Self-hosted Git LFS server that keeps large binaries off your forge's metered storage.",
|
||||
"logo": "lfsx.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/FerrLabs/LFSX",
|
||||
"website": "https://lfsx.dev",
|
||||
"docs": "https://lfsx.dev/docs/quickstart"
|
||||
},
|
||||
"tags": [
|
||||
"git",
|
||||
"developer-tools",
|
||||
"storage",
|
||||
"self-hosted"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
mounts = []
|
||||
[[config.domains]]
|
||||
serviceName = "lfsx"
|
||||
port = 8080
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
LFSX_PUBLIC_URL = "https://${main_domain}"
|
||||
LFSX_AUTH = "github"
|
||||
@@ -1,7 +1,7 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
libredb-studio:
|
||||
image: ghcr.io/libredb/libredb-studio:0.9.59
|
||||
image: ghcr.io/libredb/libredb-studio:0.18.0
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- ADMIN_EMAIL=admin@libredb.org
|
||||
@@ -11,6 +11,20 @@ services:
|
||||
- JWT_SECRET=${JWT_SECRET}
|
||||
- STORAGE_PROVIDER=sqlite
|
||||
- STORAGE_SQLITE_PATH=/app/data/libredb-storage.db
|
||||
# Dokploy's auto-generated domain is HTTP (sslip.io); keep false so login works
|
||||
- AUTH_COOKIE_SECURE=false
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"node",
|
||||
"-e",
|
||||
"fetch('http://127.0.0.1:3000/api/db/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 15s
|
||||
volumes:
|
||||
- libredb-data:/app/data
|
||||
volumes:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"id": "libredb-studio",
|
||||
"name": "LibreDB Studio",
|
||||
"version": "0.9.59",
|
||||
"description": "The modern, AI-powered open-source SQL IDE. Query PostgreSQL, MySQL, Oracle, SQL Server, SQLite, MongoDB and Redis from your browser.",
|
||||
"version": "0.18.0",
|
||||
"description": "The open-source SQL IDE you host yourself. Query PostgreSQL, MySQL, Oracle, SQL Server, MongoDB, Redis, ClickHouse, Cassandra and more from your browser, twenty-six drivers reaching fifty-four named engines.",
|
||||
"logo": "libredb-studio.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/libredb/libredb-studio",
|
||||
|
||||
@@ -8,11 +8,7 @@ jwt_secret = "${password:64}"
|
||||
serviceName = "libredb-studio"
|
||||
port = 3000
|
||||
|
||||
[[config.env]]
|
||||
[config.env]
|
||||
ADMIN_PASSWORD = "${admin_password}"
|
||||
|
||||
[[config.env]]
|
||||
USER_PASSWORD = "${user_password}"
|
||||
|
||||
[[config.env]]
|
||||
JWT_SECRET = "${jwt_secret}"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
metabase:
|
||||
image: metabase/metabase:v0.50.8
|
||||
image: metabase/metabase:v0.63.18
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- 3000
|
||||
volumes:
|
||||
- /dev/urandom:/dev/random:ro
|
||||
environment:
|
||||
@@ -9,17 +12,29 @@ services:
|
||||
MB_DB_DBNAME: metabaseappdb
|
||||
MB_DB_PORT: 5432
|
||||
MB_DB_USER: metabase
|
||||
MB_DB_PASS: mysecretpassword
|
||||
MB_DB_PASS: ${POSTGRES_PASSWORD}
|
||||
MB_DB_HOST: postgres
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: curl --fail -I http://localhost:3000/api/health || exit 1
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
postgres:
|
||||
image: postgres:14
|
||||
image: postgres:16
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: metabase
|
||||
POSTGRES_DB: metabaseappdb
|
||||
POSTGRES_PASSWORD: mysecretpassword
|
||||
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U metabase -d metabaseappdb"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
postgres-data:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"id": "metabase",
|
||||
"name": "Metabase",
|
||||
"version": "v0.50.8",
|
||||
"version": "v0.63.18",
|
||||
"description": "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.",
|
||||
"logo": "metabase.png",
|
||||
"links": {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
postgres_password = "${password:32}"
|
||||
|
||||
[config]
|
||||
env = {}
|
||||
env = [
|
||||
"POSTGRES_PASSWORD=${postgres_password}"
|
||||
]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
obscura-cdp:
|
||||
image: h4ckf0r0day/obscura:0.2.1
|
||||
restart: unless-stopped
|
||||
command: ["serve", "--host", "0.0.0.0", "--port", "9222", "--stealth", "--storage-dir", "/data"]
|
||||
expose:
|
||||
- "9222"
|
||||
volumes:
|
||||
- obscura-data:/data
|
||||
|
||||
obscura-mcp:
|
||||
image: h4ckf0r0day/obscura:0.2.1
|
||||
restart: unless-stopped
|
||||
command: ["mcp", "--http", "--host", "0.0.0.0", "--port", "3000", "--stealth"]
|
||||
environment:
|
||||
OBSCURA_MCP_ALLOWED_ORIGINS: https://${OBSCURA_HOST}
|
||||
expose:
|
||||
- "3000"
|
||||
|
||||
caddy:
|
||||
image: caddy:2.10.2-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
OBSCURA_HOST: ${OBSCURA_HOST}
|
||||
OBSCURA_API_TOKEN: ${OBSCURA_API_TOKEN}
|
||||
volumes:
|
||||
- ../files/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
depends_on:
|
||||
- obscura-cdp
|
||||
- obscura-mcp
|
||||
healthcheck:
|
||||
test: ["CMD", "wget", "-q", "-O", "/dev/null", "http://127.0.0.1:2019/config/"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
obscura-data:
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"id": "obscura",
|
||||
"name": "Obscura Browser",
|
||||
"version": "0.2.1",
|
||||
"description": "Headless browser for AI agents with authenticated remote MCP and Chrome DevTools Protocol endpoints.",
|
||||
"logo": "obscura.png",
|
||||
"links": {
|
||||
"github": "https://github.com/h4ckf0r0day/obscura",
|
||||
"website": "https://github.com/h4ckf0r0day/obscura",
|
||||
"docs": "https://github.com/h4ckf0r0day/obscura/blob/master/docs/Use-the-MCP-server.md"
|
||||
},
|
||||
"tags": [
|
||||
"ai",
|
||||
"browser",
|
||||
"automation",
|
||||
"mcp",
|
||||
"cdp"
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,62 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
api_token = "${password:48}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"OBSCURA_HOST=${main_domain}",
|
||||
"OBSCURA_API_TOKEN=${api_token}",
|
||||
]
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "caddy"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "Caddyfile"
|
||||
content = """
|
||||
:80 {
|
||||
@mcp path /mcp /mcp/*
|
||||
@cdpVersion path /json/version /json/version/
|
||||
|
||||
handle @mcp {
|
||||
@authorized header Authorization "Bearer {$OBSCURA_API_TOKEN}"
|
||||
|
||||
handle @authorized {
|
||||
reverse_proxy obscura-mcp:3000
|
||||
}
|
||||
|
||||
respond "Unauthorized" 401
|
||||
}
|
||||
|
||||
handle @cdpVersion {
|
||||
@authorized header Authorization "Bearer {$OBSCURA_API_TOKEN}"
|
||||
|
||||
handle @authorized {
|
||||
header Content-Type application/json
|
||||
respond `{"Browser":"Obscura","Protocol-Version":"1.3","webSocketDebuggerUrl":"wss://{$OBSCURA_HOST}/devtools/browser?access_token={$OBSCURA_API_TOKEN}"}` 200
|
||||
}
|
||||
|
||||
respond "Unauthorized" 401
|
||||
}
|
||||
|
||||
handle {
|
||||
@authorizedByHeader header Authorization "Bearer {$OBSCURA_API_TOKEN}"
|
||||
@authorizedByQuery {
|
||||
path /devtools/*
|
||||
query access_token={$OBSCURA_API_TOKEN}
|
||||
}
|
||||
|
||||
handle @authorizedByHeader {
|
||||
reverse_proxy obscura-cdp:9222
|
||||
}
|
||||
|
||||
handle @authorizedByQuery {
|
||||
reverse_proxy obscura-cdp:9222
|
||||
}
|
||||
|
||||
respond "Unauthorized" 401
|
||||
}
|
||||
}
|
||||
"""
|
||||
@@ -0,0 +1,40 @@
|
||||
# https://docs.opencloud.eu
|
||||
#
|
||||
# This Dokploy template for OpenCloud sets the default credentials to:
|
||||
# USERNAME: admin
|
||||
# PASSWORD: <SECRET GENERATED IN ENVIRONMENT VARIABLES>
|
||||
#
|
||||
# !!! IMPORTANT !!!
|
||||
# Please review the environment variable settings and customize
|
||||
# them prior to the first deployment!
|
||||
#
|
||||
# If you have already deployed once, changes to some environment
|
||||
# variables may not take effect (for example, the initial admin password).
|
||||
|
||||
services:
|
||||
opencloud:
|
||||
image: opencloudeu/opencloud:7.2
|
||||
restart: unless-stopped
|
||||
entrypoint: /bin/sh
|
||||
command: ["-c", "opencloud init || true; opencloud server"]
|
||||
environment:
|
||||
- OC_URL=${OC_URL:-https://localhost:9200}
|
||||
- INITIAL_ADMIN_PASSWORD=${INITIAL_ADMIN_PASSWORD:-}
|
||||
- IDM_ADMIN_PASSWORD=${IDM_ADMIN_PASSWORD:-${INITIAL_ADMIN_PASSWORD:-}}
|
||||
- PROXY_HTTP_ADDR=${PROXY_HTTP_ADDR:-0.0.0.0:9200}
|
||||
- PROXY_TLS=${PROXY_TLS:-false}
|
||||
- OC_INSECURE=${OC_INSECURE:-false}
|
||||
- OC_LOG_LEVEL=${OC_LOG_LEVEL:-info}
|
||||
volumes:
|
||||
- opencloud-config:/etc/opencloud
|
||||
- opencloud-data:/var/lib/opencloud
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "curl -f http://localhost:9200/healthz || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
start_period: 45s
|
||||
|
||||
volumes:
|
||||
opencloud-config: {}
|
||||
opencloud-data: {}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "opencloud",
|
||||
"name": "OpenCloud",
|
||||
"version": "7.2",
|
||||
"description": "Open source cloud platform for secure file sync, sharing, and content collaboration",
|
||||
"logo": "opencloud.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/opencloud-eu/opencloud",
|
||||
"website": "https://opencloud.eu",
|
||||
"docs": "https://docs.opencloud.eu"
|
||||
},
|
||||
"tags": ["Storage", "Cloud", "Files", "Productivity"]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512" viewBox="0 0 512 512">
|
||||
<rect x="0" y="0" width="512" height="512" rx="64" fill="#20434f"/>
|
||||
<polygon points="255.98 342.75 271.89 333.57 271.89 267.12 329.08 234.1 329.08 215.78 313.18 206.6 255.6 239.84 198.83 207.06 182.93 216.24 182.93 234.56 240.12 267.58 240.12 333.59 255.98 342.75" fill="#e2baff"/>
|
||||
<polygon points="401.95 150.82 256 66.56 256 66.56 256 66.56 110.05 150.82 110.05 187.5 256 103.24 401.95 187.5 401.95 150.82" fill="#e2baff"/>
|
||||
<polygon points="401.95 324.5 256 408.76 110.06 324.5 110.06 361.17 256 445.43 256 445.43 256 445.43 401.95 361.17 401.95 324.5" fill="#e2baff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 732 B |
@@ -0,0 +1,17 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
admin_password = "${password:32}"
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "opencloud"
|
||||
port = 9200
|
||||
host = "${main_domain}"
|
||||
|
||||
[config.env]
|
||||
OC_URL = "https://${main_domain}"
|
||||
INITIAL_ADMIN_PASSWORD = "${admin_password}"
|
||||
IDM_ADMIN_PASSWORD = "${admin_password}"
|
||||
PROXY_HTTP_ADDR = "0.0.0.0:9200"
|
||||
PROXY_TLS = "false"
|
||||
OC_INSECURE = "false"
|
||||
OC_LOG_LEVEL = "info"
|
||||
@@ -0,0 +1,44 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
opencode:
|
||||
image: ghcr.io/anomalyco/opencode:1.18.32
|
||||
restart: unless-stopped
|
||||
entrypoint: []
|
||||
# The official image is minimal (no git/node). Provision a toolchain on
|
||||
# start-up so the agent can clone repos and run project commands.
|
||||
# Set TOOLCHAIN_PACKAGES to "" to skip it, or to e.g. "git go" for another stack.
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
apk add --no-cache $${TOOLCHAIN_PACKAGES} >/dev/null 2>&1 || true
|
||||
if [ -n "$${GIT_TOKEN}" ] && [ -n "$${GIT_REPO_URL}" ]; then
|
||||
host=$$(printf '%s' "$${GIT_REPO_URL}" | sed -E 's#^https?://([^/]+)/.*#\1#')
|
||||
git config --global credential.helper store
|
||||
printf 'https://x-access-token:%s@%s\n' "$${GIT_TOKEN}" "$$host" > /root/.git-credentials
|
||||
fi
|
||||
if [ -n "$${GIT_REPO_URL}" ]; then
|
||||
dir="/workspace/$$(basename "$${GIT_REPO_URL}" .git)"
|
||||
[ -d "$$dir/.git" ] || git clone "$${GIT_REPO_URL}" "$$dir" || true
|
||||
fi
|
||||
exec opencode serve --hostname 0.0.0.0 --port 4096
|
||||
working_dir: /workspace
|
||||
expose:
|
||||
- 4096
|
||||
environment:
|
||||
- OPENCODE_SERVER_USERNAME=${OPENCODE_SERVER_USERNAME}
|
||||
- OPENCODE_SERVER_PASSWORD=${OPENCODE_SERVER_PASSWORD}
|
||||
- TOOLCHAIN_PACKAGES=${TOOLCHAIN_PACKAGES}
|
||||
- GIT_REPO_URL=${GIT_REPO_URL}
|
||||
- GIT_TOKEN=${GIT_TOKEN}
|
||||
volumes:
|
||||
- opencode-data:/root/.local/share/opencode
|
||||
- opencode-config:/root/.config/opencode
|
||||
- opencode-workspace:/workspace
|
||||
|
||||
volumes:
|
||||
opencode-data:
|
||||
opencode-config:
|
||||
opencode-workspace:
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"id": "opencode",
|
||||
"name": "OpenCode",
|
||||
"version": "1.18.32",
|
||||
"description": "OpenCode is an open source AI coding agent. This template runs its password-protected web UI; optionally set a repository URL to clone on start-up, then chat with, edit and run your code from the browser.",
|
||||
"logo": "opencode.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/anomalyco/opencode",
|
||||
"website": "https://opencode.ai",
|
||||
"docs": "https://opencode.ai/v2/docs"
|
||||
},
|
||||
"tags": [
|
||||
"ai",
|
||||
"coding",
|
||||
"agent",
|
||||
"development",
|
||||
"self-hosted"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="512" height="512"><svg width="512" height="512" viewBox="0 0 512 512" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="512" height="512" fill="#131010"></rect>
|
||||
<path d="M320 224V352H192V224H320Z" fill="#5A5858"></path>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M384 416H128V96H384V416ZM320 160H192V352H320V160Z" fill="white"></path>
|
||||
</svg><style>@media (prefers-color-scheme: light) { :root { filter: none; } }
|
||||
@media (prefers-color-scheme: dark) { :root { filter: none; } }
|
||||
</style></svg>
|
||||
|
After Width: | Height: | Size: 612 B |
@@ -0,0 +1,21 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
opencode_password = "${password:32}"
|
||||
|
||||
[config]
|
||||
[[config.domains]]
|
||||
serviceName = "opencode"
|
||||
port = 4096
|
||||
host = "${main_domain}"
|
||||
https = true
|
||||
|
||||
[config.env]
|
||||
OPENCODE_SERVER_USERNAME = "opencode"
|
||||
OPENCODE_SERVER_PASSWORD = "${opencode_password}"
|
||||
# Packages installed in the container at start-up (Alpine apk).
|
||||
# Set to "" to keep the minimal image, or adapt to your stack (e.g. "git go").
|
||||
TOOLCHAIN_PACKAGES = "git nodejs npm python3"
|
||||
# Optional: clone a repository into /workspace on start-up.
|
||||
# Leave GIT_REPO_URL empty to skip. Set GIT_TOKEN (GitHub PAT, Contents: read) for private repos.
|
||||
GIT_REPO_URL = ""
|
||||
GIT_TOKEN = ""
|
||||
@@ -0,0 +1,20 @@
|
||||
# OpenLivery on Dokploy
|
||||
|
||||
The first registration creates the agency owner and closes public sign-up
|
||||
Create additional client workspaces from the dashboard
|
||||
|
||||
This template uses the official OpenLivery v0.4.0 images for Linux AMD64
|
||||
Use an x86-64 server, or configure AMD64 emulation on an ARM server
|
||||
|
||||
The generated domain initially uses HTTP
|
||||
Before using real client data, configure a domain and HTTPS in Dokploy, update FRONTEND_URL to that HTTPS origin and set COOKIE_SECURE=true, then redeploy
|
||||
|
||||
Add your own AI provider credentials in OpenLivery and configure WhatsApp Cloud API, the QR bridge or the web widget
|
||||
Provider, WhatsApp and infrastructure costs are separate
|
||||
|
||||
Back up PostgreSQL, the backend-storage volume and the generated environment before upgrades
|
||||
Keep ENCRYPTION_KEY and WHATSAPP_BRIDGE_TOKEN unchanged across restarts and upgrades
|
||||
Client portals use the main domain by default
|
||||
Additional client domains require DNS and proxy configuration
|
||||
|
||||
[Self-hosting guide](https://github.com/sarrazola/openlivery/blob/main/docs/en/self-hosting.md)
|
||||
@@ -0,0 +1,99 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
openlivery:
|
||||
image: caddy:2.10.2-alpine
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ../files/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||
expose:
|
||||
- "80"
|
||||
depends_on:
|
||||
api:
|
||||
condition: service_healthy
|
||||
web:
|
||||
condition: service_healthy
|
||||
|
||||
db:
|
||||
image: postgres:17.6-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: openlivery
|
||||
POSTGRES_USER: openlivery
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U openlivery -d openlivery"]
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 20
|
||||
|
||||
api:
|
||||
image: ghcr.io/sarrazola/openlivery-api:v0.4.0
|
||||
platform: linux/amd64
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
DATABASE_URL: postgresql+psycopg://openlivery:${POSTGRES_PASSWORD}@db:5432/openlivery
|
||||
SECRET_KEY: ${SECRET_KEY}
|
||||
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
|
||||
WHATSAPP_BRIDGE_TOKEN: ${WHATSAPP_BRIDGE_TOKEN}
|
||||
FRONTEND_URL: ${FRONTEND_URL}
|
||||
COOKIE_SECURE: ${COOKIE_SECURE}
|
||||
COOKIE_SAMESITE: lax
|
||||
BACKEND_URL: http://api:8000
|
||||
WHATSAPP_BRIDGE_URL: http://whatsapp:3101
|
||||
STORAGE_DIR: /app/backend/storage
|
||||
ALLOW_MULTI_AGENCY: "false"
|
||||
volumes:
|
||||
- backend-storage:/app/backend/storage
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3)"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 30s
|
||||
|
||||
web:
|
||||
image: ghcr.io/sarrazola/openlivery-web:v0.4.0
|
||||
platform: linux/amd64
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
BACKEND_INTERNAL_URL: http://api:8000
|
||||
depends_on:
|
||||
api:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/login').then(r => { if (!r.ok) process.exit(1) }).catch(() => process.exit(1))"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 30s
|
||||
|
||||
whatsapp:
|
||||
image: ghcr.io/sarrazola/openlivery-whatsapp:v0.4.0
|
||||
platform: linux/amd64
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
BACKEND_URL: http://api:8000
|
||||
WHATSAPP_BRIDGE_HOST: 0.0.0.0
|
||||
WHATSAPP_BRIDGE_PORT: 3101
|
||||
WHATSAPP_BRIDGE_TOKEN: ${WHATSAPP_BRIDGE_TOKEN}
|
||||
WHATSAPP_STORE_URL: postgres://openlivery:${POSTGRES_PASSWORD}@db:5432/openlivery?sslmode=disable&search_path=whatsmeow
|
||||
WHATSAPP_LOG_LEVEL: silent
|
||||
depends_on:
|
||||
api:
|
||||
condition: service_healthy
|
||||
healthcheck:
|
||||
test: ["CMD", "/app/bridge", "-healthcheck"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 12
|
||||
start_period: 30s
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
backend-storage:
|
||||
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "openlivery",
|
||||
"name": "OpenLivery",
|
||||
"version": "v0.4.0",
|
||||
"description": "MIT-licensed WhatsApp AI agents for agencies, with separate client workspaces and branded portals in one self-hosted installation",
|
||||
"logo": "logo.png",
|
||||
"links": {
|
||||
"github": "https://github.com/sarrazola/openlivery",
|
||||
"website": "https://www.openlivery.com",
|
||||
"docs": "https://github.com/sarrazola/openlivery/blob/main/docs/en/self-hosting.md"
|
||||
},
|
||||
"tags": ["ai", "chat", "customer-support"]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
postgres_password = "${hash:48}"
|
||||
secret_key = "${hash:64}"
|
||||
encryption_key = "${hash:64}"
|
||||
bridge_token = "${hash:64}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"FRONTEND_URL=http://${main_domain}",
|
||||
"COOKIE_SECURE=false",
|
||||
"POSTGRES_PASSWORD=${postgres_password}",
|
||||
"SECRET_KEY=${secret_key}",
|
||||
"ENCRYPTION_KEY=${encryption_key}",
|
||||
"WHATSAPP_BRIDGE_TOKEN=${bridge_token}",
|
||||
]
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "openlivery"
|
||||
port = 80
|
||||
host = "${main_domain}"
|
||||
|
||||
[[config.mounts]]
|
||||
filePath = "./Caddyfile"
|
||||
content = """
|
||||
:80 {
|
||||
handle /api/* {
|
||||
reverse_proxy api:8000
|
||||
}
|
||||
handle {
|
||||
reverse_proxy web:3000
|
||||
}
|
||||
}
|
||||
"""
|
||||
@@ -0,0 +1,27 @@
|
||||
services:
|
||||
openlmnp:
|
||||
image: manganate06/openlmnp:1.6.6
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
# APP_URL must carry the scheme: password-reset links and e-mails are built
|
||||
# from it, and would otherwise point at http://localhost.
|
||||
APP_URL: https://${OPENLMNP_HOST}
|
||||
# Sign-up closes on its own once the first account is created.
|
||||
ALLOW_REGISTRATION: auto
|
||||
volumes:
|
||||
# SQLite database and uploaded receipts — the only two paths worth keeping.
|
||||
- openlmnp-database:/var/www/html/database
|
||||
- openlmnp-storage:/var/www/html/storage
|
||||
healthcheck:
|
||||
# Laravel health route. Tested with PHP rather than curl: the image is built on
|
||||
# Alpine and ships neither curl nor wget.
|
||||
test: ["CMD", "php", "-r", "exit(@file_get_contents('http://127.0.0.1:8000/up') === false ? 1 : 0);"]
|
||||
interval: 10s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
# First boot creates the SQLite database and runs the migrations.
|
||||
start_period: 90s
|
||||
|
||||
volumes:
|
||||
openlmnp-database:
|
||||
openlmnp-storage:
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"id": "openlmnp",
|
||||
"name": "OpenLMNP",
|
||||
"version": "1.6.6",
|
||||
"description": "Self-hosted accounting for French furnished rentals (LMNP): depreciation schedules, ready-to-file 2031/2033 tax forms, FEC export and Airbnb CSV import.",
|
||||
"logo": "openlmnp.png",
|
||||
"links": {
|
||||
"github": "https://github.com/manganate006/openlmnp",
|
||||
"website": "https://openlmnp.fr",
|
||||
"docs": "https://github.com/manganate006/openlmnp/blob/main/docs/INSTALLATION.md"
|
||||
},
|
||||
"tags": [
|
||||
"finance",
|
||||
"accounting",
|
||||
"tax",
|
||||
"rental"
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 8.2 KiB |
@@ -0,0 +1,13 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"OPENLMNP_HOST=${main_domain}",
|
||||
]
|
||||
mounts = []
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "openlmnp"
|
||||
port = 8000
|
||||
host = "${main_domain}"
|
||||
@@ -0,0 +1,131 @@
|
||||
services:
|
||||
db:
|
||||
image: postgres:17-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_DB: openproject
|
||||
POSTGRES_USER: openproject
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
|
||||
cache:
|
||||
image: memcached:1.6-alpine
|
||||
restart: unless-stopped
|
||||
|
||||
web:
|
||||
image: openproject/openproject:17-slim
|
||||
restart: unless-stopped
|
||||
command: "./docker/prod/web"
|
||||
environment: &openproject-env
|
||||
OPENPROJECT_HOST__NAME: ${OPENPROJECT_HOST__NAME}
|
||||
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
|
||||
OPENPROJECT_RAILS__CACHE__STORE: "memcache"
|
||||
OPENPROJECT_CACHE__EXPIRES__IN__SECONDS: ${OPENPROJECT_CACHE__EXPIRES__IN__SECONDS}
|
||||
OPENPROJECT_CACHE__MEMCACHE__SERVER: "cache:11211"
|
||||
OPENPROJECT_RAILS__RELATIVE__URL__ROOT: ""
|
||||
DATABASE_URL: "postgres://openproject:${POSTGRES_PASSWORD}@db:5432/openproject?pool=20&encoding=unicode&reconnect=true"
|
||||
RAILS_MIN_THREADS: ${RAILS_MIN_THREADS:-4}
|
||||
RAILS_MAX_THREADS: ${RAILS_MAX_THREADS:-16}
|
||||
OPENPROJECT_COLLABORATIVE__EDITING__HOCUSPOCUS__URL: ""
|
||||
OPENPROJECT_HTTPS: "true"
|
||||
|
||||
# IMAP
|
||||
IMAP_ENABLED: ${IMAP_ENABLED:-flase}
|
||||
IMAP_HOST: ${IMAP_HOST}
|
||||
IMAP_USERNAME: ${IMAP_USERNAME}
|
||||
IMAP_PASSWORD: ${IMAP_PASSWORD}
|
||||
IMAP_PORT: ${IMAP_PORT:-993}
|
||||
IMAP_SSL: ${IMAP_SSL:-true}
|
||||
IMAP_FOLDER: ${IMAP_FOLDER:-INBOX}
|
||||
IMAP_SSL_VERIFICATION: "true"
|
||||
IMAP_CHECK_INTERVAL: ${IMAP_CHECK_INTERVAL:-600}
|
||||
IMAP_ALLOW_OVERRIDE: ${IMAP_ALLOW_OVERRIDE}
|
||||
|
||||
#SMTP
|
||||
OPENPROJECT_EMAIL__DELIVERY__METHOD: "smtp"
|
||||
OPENPROJECT_SMTP__ADDRESS: ${SMTP_HOST}
|
||||
OPENPROJECT_SMTP__PORT: ${SMTP_PORT:-587}
|
||||
OPENPROJECT_SMTP__DOMAIN: ${SMTP_DOMAIN}
|
||||
OPENPROJECT_SMTP__AUTHENTICATION: ${SMTP_AUTHENTICATION:-plain}
|
||||
OPENPROJECT_SMTP__USER__NAME: ${SMTP_USERNAME}
|
||||
OPENPROJECT_SMTP__PASSWORD: ${SMTP_PASSWORD}
|
||||
OPENPROJECT_SMTP__ENABLE__STARTTLS__AUTO: ${SMTP_STARTTLS:-true}
|
||||
OPENPROJECT_SMTP__OPENSSL__VERIFY__MODE: ${SMTP_VERIFY_MODE:-peer}
|
||||
|
||||
# S3 Attachments
|
||||
OPENPROJECT_ATTACHMENTS__STORAGE: ${ATTACHMENTS_STORAGE:-file} # for S3, use 'fog' instead of 'file'
|
||||
OPENPROJECT_FOG_DIRECTORY: ${S3_BUCKET}
|
||||
OPENPROJECT_FOG_CREDENTIALS_PROVIDER: "AWS"
|
||||
OPENPROJECT_FOG_CREDENTIALS_AWS__ACCESS__KEY__ID: ${S3_ACCESS_KEY}
|
||||
OPENPROJECT_FOG_CREDENTIALS_AWS__SECRET__ACCESS__KEY: ${S3_SECRET_KEY}
|
||||
OPENPROJECT_FOG_CREDENTIALS_REGION: ${S3_REGION}
|
||||
OPENPROJECT_FOG_CREDENTIALS_ENDPOINT: ${S3_ENDPOINT}
|
||||
OPENPROJECT_FOG_CREDENTIALS_PATH__STYLE: "true"
|
||||
OPENPROJECT_FOG__DOWNLOAD__URL__EXPIRES__IN: "300"
|
||||
OPENPROJECT_DIRECT__UPLOADS: "true"
|
||||
|
||||
volumes:
|
||||
- opdata:/var/openproject/assets
|
||||
|
||||
depends_on:
|
||||
- db
|
||||
- cache
|
||||
- seeder
|
||||
expose:
|
||||
- "8080"
|
||||
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8080/health_checks/default"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 60s
|
||||
|
||||
seeder:
|
||||
image: openproject/openproject:17-slim
|
||||
restart: on-failure
|
||||
command: "./docker/prod/seeder"
|
||||
environment:
|
||||
<<: *openproject-env
|
||||
|
||||
volumes:
|
||||
- opdata:/var/openproject/assets
|
||||
|
||||
depends_on:
|
||||
- db
|
||||
- cache
|
||||
|
||||
worker:
|
||||
image: openproject/openproject:17-slim
|
||||
restart: unless-stopped
|
||||
command: "./docker/prod/worker"
|
||||
environment:
|
||||
<<: *openproject-env
|
||||
|
||||
volumes:
|
||||
- opdata:/var/openproject/assets
|
||||
|
||||
depends_on:
|
||||
- db
|
||||
- cache
|
||||
- seeder
|
||||
|
||||
cron:
|
||||
image: openproject/openproject:17-slim
|
||||
restart: unless-stopped
|
||||
command: "./docker/prod/cron"
|
||||
environment:
|
||||
<<: *openproject-env
|
||||
|
||||
volumes:
|
||||
- opdata:/var/openproject/assets
|
||||
|
||||
depends_on:
|
||||
- db
|
||||
- cache
|
||||
- seeder
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
opdata:
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500"><rect width="500" height="500" style="fill:#fff"/><path d="M146.66,257.09V225.82c0-9,6-14.95,14.69-14.95s14.87,6,14.87,14.95v31.27c0,9-6.07,15-14.87,15S146.66,266.06,146.66,257.09Zm23.49.34V225.57c0-5.56-3.59-9.4-8.8-9.4s-8.63,3.84-8.63,9.4v31.86c0,5.47,3.51,9.32,8.63,9.32S170.15,262.9,170.15,257.43Z"/><path d="M209.79,250c0,3.16,0,6.07-.08,9.23-.26,7.52-4.62,12.81-11.54,12.81a10,10,0,0,1-8.45-4.1c-.26-.25-.43-.08-.43.17v19a.81.81,0,0,1-.86.85h-4.35a.81.81,0,0,1-.86-.85V229a.81.81,0,0,1,.86-.85h4.35a.81.81,0,0,1,.86.85v2.48c0,.26.17.43.43.17a10.06,10.06,0,0,1,8.45-4.19c6.84,0,11.2,5.47,11.54,13.08C209.79,243.68,209.79,246.84,209.79,250Zm-6.06,8.2c.08-3,0-6.23,0-8.46v-8.45c.08-5-2.91-8.55-7.35-8.55-4.1,0-7.09,3.42-7.09,8.55V258.2c0,5.13,3,8.55,7.09,8.55C200.74,266.75,203.81,263.33,203.73,258.2Z"/><path d="M240.81,252.05H221.33a.37.37,0,0,0-.34.34v6.24c0,4.78,3,8.12,7.26,8.12a7.18,7.18,0,0,0,7.17-6c.17-.52.43-.77.94-.77l4.28.17a.76.76,0,0,1,.76.94c-.94,6.66-5.63,10.93-13.15,10.93-8.12,0-13.33-5.21-13.33-13.07V240.52c0-7.69,5.21-13.08,13.33-13.08,8.28,0,13.41,5.39,13.41,13.08V251.2A.8.8,0,0,1,240.81,252.05ZM221,240.94v5.73a.37.37,0,0,0,.34.34h14a.37.37,0,0,0,.34-.34v-5.73c0-4.87-3-8.2-7.43-8.2C224,232.74,221,236.07,221,240.94Z"/><path d="M273.53,241.11V270.5a.81.81,0,0,1-.86.86h-4.35a.81.81,0,0,1-.86-.86V241.63c0-5.56-2.48-8.8-6.83-8.8-4.53,0-7.35,3.5-7.35,9.22V270.5a.81.81,0,0,1-.85.86h-4.36a.81.81,0,0,1-.86-.86V229a.81.81,0,0,1,.86-.85h4.36a.8.8,0,0,1,.85.85v2.82c0,.26.26.34.43.09a10.53,10.53,0,0,1,8.88-4.36C269.6,227.53,273.53,232.57,273.53,241.11Z"/><path d="M309.75,228c0,9.83-5.46,16.32-13.67,16.32h-8.8a.37.37,0,0,0-.34.34v25.8a.81.81,0,0,1-.85.86h-4.36a.81.81,0,0,1-.85-.86V212.32a.8.8,0,0,1,.85-.85h14.1C304.2,211.47,309.75,218.13,309.75,228Zm-6.06.09c0-6.92-3.51-11.36-8.63-11.36h-7.78a.37.37,0,0,0-.34.34v21.95a.37.37,0,0,0,.34.35h7.78C300.18,239.41,303.69,235.05,303.69,228.13Z"/><path d="M332.65,228.64a1,1,0,0,1,.43,1.11l-.94,4.36c-.09.6-.52.68-1.11.51a6.07,6.07,0,0,0-3.59-.34c-4.1.26-7,4.78-7,10.59V270.5a.81.81,0,0,1-.85.86h-4.36a.81.81,0,0,1-.85-.86V229a.8.8,0,0,1,.85-.85h4.36a.8.8,0,0,1,.85.85v5.13c0,.34.26.34.43.08,1.45-3.84,4-6.49,8.12-6.49A6.79,6.79,0,0,1,332.65,228.64Z"/><path d="M334.7,259V240.52c0-7.69,5.21-13.08,13.41-13.08s13.5,5.39,13.5,13.08V259c0,7.86-5.29,13.07-13.5,13.07S334.7,266.83,334.7,259Zm20.85-.34V240.94c0-4.87-3.08-8.2-7.44-8.2s-7.34,3.33-7.34,8.2v17.69c0,4.78,3,8.12,7.34,8.12S355.55,263.41,355.55,258.63Z"/><path d="M359.82,288.28v-3.59a.81.81,0,0,1,.85-.86c4.87-.17,6.92-3.5,6.92-8.71V229a.81.81,0,0,1,.86-.85h4.35a.81.81,0,0,1,.86.85v46.14c0,8.54-2.31,14-13,14A.8.8,0,0,1,359.82,288.28Zm6.58-72.37a4.23,4.23,0,1,1,8.45,0,4.23,4.23,0,1,1-8.45,0Z"/><path d="M405.61,252.05H386.13a.37.37,0,0,0-.34.34v6.24c0,4.78,3,8.12,7.26,8.12a7.19,7.19,0,0,0,7.18-6c.17-.52.43-.77.94-.77l4.27.17a.77.77,0,0,1,.77.94c-.94,6.66-5.64,10.93-13.16,10.93-8.11,0-13.33-5.21-13.33-13.07V240.52c0-7.69,5.22-13.08,13.33-13.08,8.29,0,13.42,5.39,13.42,13.08V251.2A.81.81,0,0,1,405.61,252.05Zm-19.82-11.11v5.73a.37.37,0,0,0,.34.34h14a.37.37,0,0,0,.34-.34v-5.73c0-4.87-3-8.2-7.43-8.2C388.78,232.74,385.79,236.07,385.79,240.94Z"/><path d="M411.08,259V240.52c0-7.61,5.21-13.08,13.33-13.08s13.5,5.47,13.5,11.88v.85a.81.81,0,0,1-.86.86l-4.36.17a.79.79,0,0,1-.85-.77v-.68a7.14,7.14,0,0,0-7.43-7c-4.28,0-7.27,3.33-7.27,8.2v17.6c0,4.87,3,8.21,7.27,8.21a7.14,7.14,0,0,0,7.43-7v-.6a.8.8,0,0,1,.85-.85l4.36.08a.81.81,0,0,1,.86.86v.94c0,6.66-5.3,11.87-13.5,11.87S411.08,266.83,411.08,259Z"/><path d="M457,232.66h-6.49a.37.37,0,0,0-.34.34v26.14c0,5.47,2,6.92,5.55,6.84h.86a.8.8,0,0,1,.85.85v3.67a.81.81,0,0,1-.85.86h-2.14c-6.07,0-10.25-1.79-10.25-10.42V233a.37.37,0,0,0-.34-.34h-3.34a.81.81,0,0,1-.85-.86V229a.8.8,0,0,1,.85-.85h3.34a.37.37,0,0,0,.34-.34v-9.57a.81.81,0,0,1,.85-.86h4.27a.81.81,0,0,1,.86.86v9.57a.37.37,0,0,0,.34.34H457a.81.81,0,0,1,.86.85v2.82A.81.81,0,0,1,457,232.66Z"/><path d="M84.21,270.12h0a.58.58,0,0,0,0,.1,4.58,4.58,0,0,0,9.15,0v-.11h0v-11H84.21Z" style="fill:#0770b8"/><path d="M105.57,210.93h-6.1a15.18,15.18,0,0,0-15.26,15.16v18.4H57.36A15.18,15.18,0,0,0,42.1,259.66v12.2a15.33,15.33,0,0,0,15.26,15.35h6.1a15.33,15.33,0,0,0,15.26-15.35v-12.2a4.55,4.55,0,0,0,0-.52H69.56v12.72a6.18,6.18,0,0,1-6.1,6.2h-6.1a6.18,6.18,0,0,1-6.1-6.2v-12.2a6,6,0,0,1,6.1-6h48.21a15.33,15.33,0,0,0,15.26-15.34V226.09A15.18,15.18,0,0,0,105.57,210.93Zm6.1,27.37a6.19,6.19,0,0,1-6.1,6.19H93.36v-18.4a6,6,0,0,1,6.11-6h6.1a6,6,0,0,1,6.1,6Z" style="fill:#0770b8"/></svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"id": "openproject",
|
||||
"name": "OpenProject",
|
||||
"version": "17",
|
||||
"description": "OpenProject 17 slim: web, worker, cron, Postgres 17, memcached. Optional IMAP/SMTP and S3.",
|
||||
"logo": "logo.svg",
|
||||
"links": {
|
||||
"github": "https://github.com/opf/openproject",
|
||||
"website": "https://www.openproject.org/",
|
||||
"docs": "https://www.openproject.org/docs/installation-and-operations/installation/docker-compose/"
|
||||
},
|
||||
"tags": ["project-management", "productivity"]
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
[variables]
|
||||
main_domain = "${domain}"
|
||||
postgres_password = "${password:32}"
|
||||
secret_key_base = "${base64:64}"
|
||||
|
||||
[config]
|
||||
env = [
|
||||
"OPENPROJECT_HOST__NAME=${main_domain}",
|
||||
"POSTGRES_PASSWORD=${postgres_password}",
|
||||
"SECRET_KEY_BASE=${secret_key_base}",
|
||||
"OPENPROJECT_HTTPS=true",
|
||||
"OPENPROJECT_CACHE__EXPIRES__IN__SECONDS=3600",
|
||||
"SMTP_HOST=",
|
||||
"SMTP_PORT=587",
|
||||
"SMTP_DOMAIN=",
|
||||
"SMTP_USERNAME=",
|
||||
"SMTP_PASSWORD=",
|
||||
"SMTP_AUTHENTICATION=login",
|
||||
"SMTP_STARTTLS=true",
|
||||
"SMTP_VERIFY_MODE=peer",
|
||||
"IMAP_ENABLED=false",
|
||||
"IMAP_ALLOW_OVERRIDE=type,project,priority,status,responsible,assigned_to,estimated_hours,remaining_hours",
|
||||
"IMAP_HOST=",
|
||||
"IMAP_PORT=993",
|
||||
"IMAP_USERNAME=",
|
||||
"IMAP_PASSWORD=",
|
||||
"IMAP_CHECK_INTERVAL=600",
|
||||
"ATTACHMENTS_STORAGE=file",
|
||||
"S3_BUCKET=",
|
||||
"S3_ACCESS_KEY=",
|
||||
"S3_SECRET_KEY=",
|
||||
"S3_REGION=",
|
||||
"S3_ENDPOINT=",
|
||||
]
|
||||
|
||||
[[config.domains]]
|
||||
serviceName = "web"
|
||||
port = 8080
|
||||
host = "${main_domain}"
|
||||
@@ -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:
|
||||
@@ -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.
|
||||