diff --git a/.github/sponsors/synexa.png b/.github/sponsors/synexa.png
new file mode 100644
index 000000000..737ccd576
Binary files /dev/null and b/.github/sponsors/synexa.png differ
diff --git a/README.md b/README.md
index 8d3755490..9246cf556 100644
--- a/README.md
+++ b/README.md
@@ -74,6 +74,8 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
+
+
### Premium Supporters 🥇
@@ -94,8 +96,10 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com).
+
+
### Community Backers 🤝
diff --git a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx
index ee674e0fe..48ec4557b 100644
--- a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx
+++ b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx
@@ -47,23 +47,12 @@ export function TerminalLine({ log, noTimestamp, searchTerm }: LogLineProps) {
}
const htmlContent = fancyAnsi.toHtml(text);
+ const searchRegex = new RegExp(`(${escapeRegExp(term)})`, "gi");
+
const modifiedContent = htmlContent.replace(
- /
]*)>([^<]*)<\/span>/g,
- (match, attrs, content) => {
- const searchRegex = new RegExp(`(${escapeRegExp(term)})`, "gi");
- if (!content.match(searchRegex)) return match;
-
- const segments = content.split(searchRegex);
- const wrappedSegments = segments
- .map((segment: string) =>
- segment.toLowerCase() === term.toLowerCase()
- ? `${segment}`
- : segment,
- )
- .join("");
-
- return `${wrappedSegments}`;
- },
+ searchRegex,
+ (match) =>
+ `${match}`,
);
return (
diff --git a/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx b/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx
index 5f693707f..8cce306a5 100644
--- a/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx
+++ b/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx
@@ -145,15 +145,6 @@ export const SecurityAudit = ({ serverId }: Props) => {
: "Enabled (Password Authentication should be disabled)"
}
/>
-
+
\ No newline at end of file
diff --git a/apps/dokploy/public/templates/pocket-id.svg b/apps/dokploy/public/templates/pocket-id.svg
new file mode 100644
index 000000000..0ee89b14b
--- /dev/null
+++ b/apps/dokploy/public/templates/pocket-id.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/apps/dokploy/public/templates/wikijs.svg b/apps/dokploy/public/templates/wikijs.svg
new file mode 100644
index 000000000..78073b234
--- /dev/null
+++ b/apps/dokploy/public/templates/wikijs.svg
@@ -0,0 +1,119 @@
+
+
+
\ No newline at end of file
diff --git a/apps/dokploy/templates/convex/index.ts b/apps/dokploy/templates/convex/index.ts
index 6a112cdee..badfe7320 100644
--- a/apps/dokploy/templates/convex/index.ts
+++ b/apps/dokploy/templates/convex/index.ts
@@ -1,38 +1,38 @@
import {
- type DomainSchema,
- type Schema,
- type Template,
- generateRandomDomain,
+ type DomainSchema,
+ type Schema,
+ type Template,
+ generateRandomDomain,
} from "../utils";
export function generate(schema: Schema): Template {
- const dashboardDomain = generateRandomDomain(schema);
- const backendDomain = generateRandomDomain(schema);
- const actionsDomain = generateRandomDomain(schema);
+ const dashboardDomain = generateRandomDomain(schema);
+ const backendDomain = generateRandomDomain(schema);
+ const actionsDomain = generateRandomDomain(schema);
- const domains: DomainSchema[] = [
- {
- host: dashboardDomain,
- port: 6791,
- serviceName: "dashboard",
- },
- {
- host: backendDomain,
- port: 3210,
- serviceName: "backend",
- },
- {
- host: actionsDomain,
- port: 3211,
- serviceName: "backend",
- },
- ];
+ const domains: DomainSchema[] = [
+ {
+ host: dashboardDomain,
+ port: 6791,
+ serviceName: "dashboard",
+ },
+ {
+ host: backendDomain,
+ port: 3210,
+ serviceName: "backend",
+ },
+ {
+ host: actionsDomain,
+ port: 3211,
+ serviceName: "backend",
+ },
+ ];
- const envs = [
- `NEXT_PUBLIC_DEPLOYMENT_URL=http://${backendDomain}`,
- `CONVEX_CLOUD_ORIGIN=http://${backendDomain}`,
- `CONVEX_SITE_ORIGIN=http://${actionsDomain}`,
- ];
+ const envs = [
+ `NEXT_PUBLIC_DEPLOYMENT_URL=http://${backendDomain}`,
+ `CONVEX_CLOUD_ORIGIN=http://${backendDomain}`,
+ `CONVEX_SITE_ORIGIN=http://${actionsDomain}`,
+ ];
- return { envs, domains };
+ return { envs, domains };
}
diff --git a/apps/dokploy/templates/glance/docker-compose.yml b/apps/dokploy/templates/glance/docker-compose.yml
index e931d6e40..ace8bc940 100644
--- a/apps/dokploy/templates/glance/docker-compose.yml
+++ b/apps/dokploy/templates/glance/docker-compose.yml
@@ -2,7 +2,10 @@ services:
glance:
image: glanceapp/glance
volumes:
- - ../files/app/glance.yml:/app/glance.yml
+ - ../files/app/config/:/app/config
+ - ../files/app/assets:/app/assets
+ # Optionally, also mount docker socket if you want to use the docker containers widget
+ # - /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- 8080
- restart: unless-stopped
+ env_file: .env
\ No newline at end of file
diff --git a/apps/dokploy/templates/glance/index.ts b/apps/dokploy/templates/glance/index.ts
index 4b2297864..a0ab1b676 100644
--- a/apps/dokploy/templates/glance/index.ts
+++ b/apps/dokploy/templates/glance/index.ts
@@ -17,7 +17,7 @@ export function generate(schema: Schema): Template {
const mounts: Template["mounts"] = [
{
- filePath: "/app/glance.yml",
+ filePath: "/app/config/glance.yml",
content: `
branding:
hide-footer: true
diff --git a/apps/dokploy/templates/linkwarden/docker-compose.yml b/apps/dokploy/templates/linkwarden/docker-compose.yml
new file mode 100644
index 000000000..05ffb8a0a
--- /dev/null
+++ b/apps/dokploy/templates/linkwarden/docker-compose.yml
@@ -0,0 +1,40 @@
+services:
+ linkwarden:
+ environment:
+ - NEXTAUTH_SECRET
+ - NEXTAUTH_URL
+ - DATABASE_URL=postgresql://linkwarden:${POSTGRES_PASSWORD}@postgres:5432/linkwarden
+ restart: unless-stopped
+ image: ghcr.io/linkwarden/linkwarden:v2.9.3
+ ports:
+ - 3000
+ volumes:
+ - linkwarden-data:/data/data
+ depends_on:
+ - postgres
+ healthcheck:
+ test: curl --fail http://localhost:3000 || exit 1
+ interval: 60s
+ retries: 2
+ start_period: 60s
+ timeout: 15s
+
+ postgres:
+ image: postgres:17-alpine
+ restart: unless-stopped
+ user: postgres
+ environment:
+ POSTGRES_USER: linkwarden
+ POSTGRES_DB: linkwarden
+ POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
+ volumes:
+ - postgres-data:/var/lib/postgresql/data
+ healthcheck:
+ test: ["CMD-SHELL", "pg_isready"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+volumes:
+ linkwarden-data:
+ postgres-data:
diff --git a/apps/dokploy/templates/linkwarden/index.ts b/apps/dokploy/templates/linkwarden/index.ts
new file mode 100644
index 000000000..860250356
--- /dev/null
+++ b/apps/dokploy/templates/linkwarden/index.ts
@@ -0,0 +1,33 @@
+import {
+ type DomainSchema,
+ type Schema,
+ type Template,
+ generateBase64,
+ generatePassword,
+ generateRandomDomain,
+} from "../utils";
+
+export function generate(schema: Schema): Template {
+ const mainDomain = generateRandomDomain(schema);
+ const postgresPassword = generatePassword();
+ const nextSecret = generateBase64(32);
+
+ const domains: DomainSchema[] = [
+ {
+ host: mainDomain,
+ port: 3000,
+ serviceName: "linkwarden",
+ },
+ ];
+
+ const envs = [
+ `POSTGRES_PASSWORD=${postgresPassword}`,
+ `NEXTAUTH_SECRET=${nextSecret}`,
+ `NEXTAUTH_URL=http://${mainDomain}/api/v1/auth`,
+ ];
+
+ return {
+ domains,
+ envs,
+ };
+}
diff --git a/apps/dokploy/templates/mailpit/docker-compose.yml b/apps/dokploy/templates/mailpit/docker-compose.yml
new file mode 100644
index 000000000..d0dbdb8ec
--- /dev/null
+++ b/apps/dokploy/templates/mailpit/docker-compose.yml
@@ -0,0 +1,25 @@
+services:
+ mailpit:
+ image: axllent/mailpit:v1.22.3
+ restart: unless-stopped
+ ports:
+ - '1025:1025'
+ volumes:
+ - 'mailpit-data:/data'
+ environment:
+ - MP_SMTP_AUTH_ALLOW_INSECURE=true
+ - MP_MAX_MESSAGES=5000
+ - MP_DATABASE=/data/mailpit.db
+ - MP_UI_AUTH=${MP_UI_AUTH}
+ - MP_SMTP_AUTH=${MP_SMTP_AUTH}
+ healthcheck:
+ test:
+ - CMD
+ - /mailpit
+ - readyz
+ interval: 5s
+ timeout: 20s
+ retries: 10
+
+volumes:
+ mailpit-data:
\ No newline at end of file
diff --git a/apps/dokploy/templates/mailpit/index.ts b/apps/dokploy/templates/mailpit/index.ts
new file mode 100644
index 000000000..25f18f7e6
--- /dev/null
+++ b/apps/dokploy/templates/mailpit/index.ts
@@ -0,0 +1,31 @@
+import {
+ type DomainSchema,
+ type Schema,
+ type Template,
+ generateBase64,
+ generatePassword,
+ generateRandomDomain,
+} from "../utils";
+
+export function generate(schema: Schema): Template {
+ const domains: DomainSchema[] = [
+ {
+ host: generateRandomDomain(schema),
+ port: 8025,
+ serviceName: "mailpit",
+ },
+ ];
+
+ const defaultPassword = generatePassword();
+
+ const envs = [
+ "# Uncomment below if you want basic auth on UI and SMTP",
+ `#MP_UI_AUTH=mailpit:${defaultPassword}`,
+ `#MP_SMTP_AUTH=mailpit:${defaultPassword}`,
+ ];
+
+ return {
+ domains,
+ envs,
+ };
+}
diff --git a/apps/dokploy/templates/pocket-id/docker-compose.yml b/apps/dokploy/templates/pocket-id/docker-compose.yml
new file mode 100644
index 000000000..f93851430
--- /dev/null
+++ b/apps/dokploy/templates/pocket-id/docker-compose.yml
@@ -0,0 +1,21 @@
+services:
+ pocket-id:
+ image: ghcr.io/pocket-id/pocket-id:v0.35.1
+ restart: unless-stopped
+ environment:
+ - PUBLIC_UI_CONFIG_DISABLED
+ - PUBLIC_APP_URL
+ - TRUST_PROXY
+ ports:
+ - 80
+ volumes:
+ - pocket-id-data:/app/backend/data
+ healthcheck:
+ test: "curl -f http://localhost/health"
+ interval: 1m30s
+ timeout: 5s
+ retries: 2
+ start_period: 10s
+
+volumes:
+ pocket-id-data:
diff --git a/apps/dokploy/templates/pocket-id/index.ts b/apps/dokploy/templates/pocket-id/index.ts
new file mode 100644
index 000000000..9a9faa2a3
--- /dev/null
+++ b/apps/dokploy/templates/pocket-id/index.ts
@@ -0,0 +1,29 @@
+import {
+ type DomainSchema,
+ type Schema,
+ type Template,
+ generateRandomDomain,
+} from "../utils";
+
+export function generate(schema: Schema): Template {
+ const mainDomain = generateRandomDomain(schema);
+
+ const domains: DomainSchema[] = [
+ {
+ host: mainDomain,
+ port: 80,
+ serviceName: "pocket-id",
+ },
+ ];
+
+ const envs = [
+ "PUBLIC_UI_CONFIG_DISABLED=false",
+ `PUBLIC_APP_URL=http://${mainDomain}`,
+ "TRUST_PROXY=true",
+ ];
+
+ return {
+ domains,
+ envs,
+ };
+}
diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts
index 31668a6f9..d39465a8e 100644
--- a/apps/dokploy/templates/templates.ts
+++ b/apps/dokploy/templates/templates.ts
@@ -25,8 +25,8 @@ export const templates: TemplateData[] = [
"Outline is a self-hosted knowledge base and documentation platform that allows you to build and manage your own knowledge base applications.",
links: {
github: "https://github.com/outline/outline",
- website: "https://outline.com/",
- docs: "https://docs.outline.com/",
+ website: "https://getoutline.com/",
+ docs: "https://docs.getoutline.com/s/guide",
},
logo: "outline.png",
load: () => import("./outline/index").then((m) => m.generate),
@@ -393,6 +393,21 @@ export const templates: TemplateData[] = [
tags: ["chat"],
load: () => import("./open-webui/index").then((m) => m.generate),
},
+ {
+ id: "mailpit",
+ name: "Mailpit",
+ version: "v1.22.3",
+ description:
+ "Mailpit is a tiny, self-contained, and secure email & SMTP testing tool with API for developers.",
+ logo: "mailpit.svg",
+ links: {
+ github: "https://github.com/axllent/mailpit",
+ website: "https://mailpit.axllent.org/",
+ docs: "https://mailpit.axllent.org/docs/",
+ },
+ tags: ["email", "smtp"],
+ load: () => import("./mailpit/index").then((m) => m.generate),
+ },
{
id: "listmonk",
name: "Listmonk",
@@ -426,7 +441,7 @@ export const templates: TemplateData[] = [
{
id: "umami",
name: "Umami",
- version: "v2.14.0",
+ version: "v2.16.1",
description:
"Umami is a simple, fast, privacy-focused alternative to Google Analytics.",
logo: "umami.png",
@@ -662,6 +677,21 @@ export const templates: TemplateData[] = [
tags: ["open-source"],
load: () => import("./vaultwarden/index").then((m) => m.generate),
},
+ {
+ id: "linkwarden",
+ name: "Linkwarden",
+ version: "2.9.3",
+ description:
+ "Self-hosted, open-source collaborative bookmark manager to collect, organize and archive webpages.",
+ logo: "linkwarden.png",
+ links: {
+ github: "https://github.com/linkwarden/linkwarden",
+ website: "https://linkwarden.app/",
+ docs: "https://docs.linkwarden.app/",
+ },
+ tags: ["bookmarks", "link-sharing"],
+ load: () => import("./linkwarden/index").then((m) => m.generate),
+ },
{
id: "hi-events",
name: "Hi.events",
@@ -1093,6 +1123,21 @@ export const templates: TemplateData[] = [
tags: ["identity", "auth"],
load: () => import("./logto/index").then((m) => m.generate),
},
+ {
+ id: "pocket-id",
+ name: "Pocket ID",
+ version: "0.35.1",
+ description:
+ "A simple and easy-to-use OIDC provider that allows users to authenticate with their passkeys to your services.",
+ logo: "pocket-id.svg",
+ links: {
+ github: "https://github.com/pocket-id/pocket-id",
+ website: "https://pocket-id.org/",
+ docs: "https://pocket-id.org/docs",
+ },
+ tags: ["identity", "auth"],
+ load: () => import("./pocket-id/index").then((m) => m.generate),
+ },
{
id: "penpot",
name: "Penpot",
@@ -1559,4 +1604,18 @@ export const templates: TemplateData[] = [
tags: ["backend", "database", "api"],
load: () => import("./convex/index").then((m) => m.generate),
},
+ {
+ id: "wikijs",
+ name: "Wiki.js",
+ version: "2.5",
+ description: "The most powerful and extensible open source Wiki software.",
+ logo: "wikijs.svg",
+ links: {
+ github: "https://github.com/requarks/wiki",
+ website: "https://js.wiki/",
+ docs: "https://docs.requarks.io/",
+ },
+ tags: ["knowledge-base", "self-hosted", "documentation"],
+ load: () => import("./wikijs/index").then((m) => m.generate),
+ },
];
diff --git a/apps/dokploy/templates/umami/docker-compose.yml b/apps/dokploy/templates/umami/docker-compose.yml
index 875681658..26efd337c 100644
--- a/apps/dokploy/templates/umami/docker-compose.yml
+++ b/apps/dokploy/templates/umami/docker-compose.yml
@@ -1,6 +1,6 @@
services:
umami:
- image: ghcr.io/umami-software/umami:postgresql-v2.14.0
+ image: ghcr.io/umami-software/umami:postgresql-v2.16.1
restart: always
healthcheck:
test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"]
diff --git a/apps/dokploy/templates/wikijs/docker-compose.yml b/apps/dokploy/templates/wikijs/docker-compose.yml
new file mode 100644
index 000000000..6b21423d1
--- /dev/null
+++ b/apps/dokploy/templates/wikijs/docker-compose.yml
@@ -0,0 +1,31 @@
+version: '3.5'
+services:
+ wiki:
+ image: ghcr.io/requarks/wiki:2.5
+ restart: unless-stopped
+ environment:
+ - DB_TYPE
+ - DB_HOST
+ - DB_PORT
+ - DB_USER
+ - DB_PASS
+ - DB_NAME
+ depends_on:
+ - db
+ labels:
+ - traefik.enable=true
+ - traefik.constraint-label-stack=wikijs
+ db:
+ image: postgres:14
+ restart: unless-stopped
+ environment:
+ - POSTGRES_USER
+ - POSTGRES_PASSWORD
+ - POSTGRES_DB
+ volumes:
+ - wiki-db-data:/var/lib/postgresql/data
+networks:
+ dokploy-network:
+ external: true
+volumes:
+ wiki-db-data:
diff --git a/apps/dokploy/templates/wikijs/index.ts b/apps/dokploy/templates/wikijs/index.ts
new file mode 100644
index 000000000..ff6c234de
--- /dev/null
+++ b/apps/dokploy/templates/wikijs/index.ts
@@ -0,0 +1,35 @@
+import {
+ type DomainSchema,
+ type Schema,
+ type Template,
+ generateRandomDomain,
+} from "../utils";
+
+export function generate(schema: Schema): Template {
+ const domains: DomainSchema[] = [
+ {
+ host: generateRandomDomain(schema),
+ port: 3000,
+ serviceName: "wiki",
+ },
+ ];
+
+ const envs = [
+ "# Database Setup",
+ "POSTGRES_USER=wikijs",
+ "POSTGRES_PASSWORD=wikijsrocks",
+ "POSTGRES_DB=wiki",
+ "# WikiJS Database Connection",
+ "DB_TYPE=postgres",
+ "DB_HOST=db",
+ "DB_PORT=5432",
+ "DB_USER=wikijs",
+ "DB_PASS=wikijsrocks",
+ "DB_NAME=wiki",
+ ];
+
+ return {
+ domains,
+ envs,
+ };
+}