Phase 25: templates as stack folders (0.31.0)

Templates are now stack-shaped folders (compose.yaml + .env.example +
template.json) instead of DB rows + manifest.json + {{VAR}} rendering.
Pull copies the folder into a new stack; custom templates persist under
DATA_DIR/templates. Adds POST /api/templates/from-stack and a one-time
startup migration for pre-0.31 DB templates (drops the template table).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
menzelj
2026-06-12 07:29:54 +00:00
co-authored by Claude Fable 5
parent 34cb215266
commit 1609b8bcc3
29 changed files with 564 additions and 329 deletions
+5
View File
@@ -0,0 +1,5 @@
PUID=1000
PGID=1000
DATA_PATH=/srv/gitea
HTTP_PORT=3000
SSH_PORT=2222
@@ -4,12 +4,12 @@ services:
container_name: gitea
restart: unless-stopped
environment:
- USER_UID={{PUID}}
- USER_GID={{PGID}}
- USER_UID=${PUID:-1000}
- USER_GID=${PGID:-1000}
ports:
- "{{HTTP_PORT}}:3000"
- "{{SSH_PORT}}:22"
- "${HTTP_PORT:-3000}:3000"
- "${SSH_PORT:-2222}:22"
volumes:
- {{DATA_PATH}}:/data
- ${DATA_PATH:-/srv/gitea}:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
+9
View File
@@ -0,0 +1,9 @@
{
"name": "Gitea",
"description": "Lightweight self-hosted Git service.",
"tags": [
"git",
"dev"
],
"gpu": null
}
-14
View File
@@ -1,14 +0,0 @@
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
environment:
- PUID={{PUID}}
- PGID={{PGID}}
- TZ={{TZ}}
ports:
- "{{HTTP_PORT}}:8096"
volumes:
- {{CONFIG_PATH}}:/config
- {{MEDIA_PATH}}:/media
+6
View File
@@ -0,0 +1,6 @@
PUID=1000
PGID=1000
TZ=Europe/Berlin
CONFIG_PATH=/srv/jellyfin/config
MEDIA_PATH=/srv/media
HTTP_PORT=8096
+14
View File
@@ -0,0 +1,14 @@
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
environment:
- PUID=${PUID:-1000}
- PGID=${PGID:-1000}
- TZ=${TZ:-Europe/Berlin}
ports:
- "${HTTP_PORT:-8096}:8096"
volumes:
- ${CONFIG_PATH:-/srv/jellyfin/config}:/config
- ${MEDIA_PATH:-/srv/media}:/media
+9
View File
@@ -0,0 +1,9 @@
{
"name": "Jellyfin",
"description": "Free media streaming server with optional hardware transcoding.",
"tags": [
"media",
"streaming"
],
"gpu": "NVIDIA / Intel optional"
}
-74
View File
@@ -1,74 +0,0 @@
[
{
"id": "jellyfin",
"name": "Jellyfin",
"description": "Free media streaming server with optional hardware transcoding.",
"tags": ["media", "streaming"],
"gpu": "NVIDIA / Intel optional",
"file": "jellyfin.yaml",
"variables": [
{ "name": "PUID", "description": "User ID", "default": "1000" },
{ "name": "PGID", "description": "Group ID", "default": "1000" },
{ "name": "TZ", "description": "Timezone", "default": "Europe/Berlin" },
{ "name": "CONFIG_PATH", "description": "Host path for config", "default": "/srv/jellyfin/config" },
{ "name": "MEDIA_PATH", "description": "Host path to media library", "default": "/srv/media" },
{ "name": "HTTP_PORT", "description": "Web UI port", "default": "8096" }
]
},
{
"id": "vaultwarden",
"name": "Vaultwarden",
"description": "Lightweight Bitwarden-compatible password manager.",
"tags": ["password-manager", "security"],
"gpu": null,
"file": "vaultwarden.yaml",
"variables": [
{ "name": "TZ", "description": "Timezone", "default": "Europe/Berlin" },
{ "name": "DATA_PATH", "description": "Host path for data", "default": "/srv/vaultwarden" },
{ "name": "HTTP_PORT", "description": "Web UI port", "default": "8200" },
{ "name": "ADMIN_TOKEN", "description": "Admin panel token", "default": "change-me" }
]
},
{
"id": "uptime-kuma",
"name": "Uptime Kuma",
"description": "Self-hosted uptime / status monitoring tool.",
"tags": ["monitoring"],
"gpu": null,
"file": "uptime-kuma.yaml",
"variables": [
{ "name": "DATA_PATH", "description": "Host path for data", "default": "/srv/uptime-kuma" },
{ "name": "HTTP_PORT", "description": "Web UI port", "default": "3001" }
]
},
{
"id": "paperless-ngx",
"name": "Paperless-NGX",
"description": "Document management system that indexes scanned documents.",
"tags": ["documents"],
"gpu": null,
"file": "paperless-ngx.yaml",
"variables": [
{ "name": "TZ", "description": "Timezone", "default": "Europe/Berlin" },
{ "name": "DATA_PATH", "description": "Host path base", "default": "/srv/paperless" },
{ "name": "HTTP_PORT", "description": "Web UI port", "default": "8000" },
{ "name": "ADMIN_USER", "description": "Admin username", "default": "admin" },
{ "name": "ADMIN_PASSWORD", "description": "Admin password", "default": "change-me" }
]
},
{
"id": "gitea",
"name": "Gitea",
"description": "Lightweight self-hosted Git service.",
"tags": ["git", "dev"],
"gpu": null,
"file": "gitea.yaml",
"variables": [
{ "name": "PUID", "description": "User ID", "default": "1000" },
{ "name": "PGID", "description": "Group ID", "default": "1000" },
{ "name": "DATA_PATH", "description": "Host path for data", "default": "/srv/gitea" },
{ "name": "HTTP_PORT", "description": "Web UI port", "default": "3000" },
{ "name": "SSH_PORT", "description": "SSH port", "default": "2222" }
]
}
]
@@ -0,0 +1,5 @@
TZ=Europe/Berlin
DATA_PATH=/srv/paperless
HTTP_PORT=8000
ADMIN_USER=admin
ADMIN_PASSWORD=change-me
@@ -4,7 +4,7 @@ services:
container_name: paperless-redis
restart: unless-stopped
volumes:
- {{DATA_PATH}}/redis:/data
- ${DATA_PATH:-/srv/paperless}/redis:/data
db:
image: postgres:16-alpine
@@ -15,7 +15,7 @@ services:
- POSTGRES_USER=paperless
- POSTGRES_PASSWORD=paperless
volumes:
- {{DATA_PATH}}/db:/var/lib/postgresql/data
- ${DATA_PATH:-/srv/paperless}/db:/var/lib/postgresql/data
webserver:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
@@ -27,12 +27,12 @@ services:
environment:
- PAPERLESS_REDIS=redis://broker:6379
- PAPERLESS_DBHOST=db
- PAPERLESS_TIME_ZONE={{TZ}}
- PAPERLESS_ADMIN_USER={{ADMIN_USER}}
- PAPERLESS_ADMIN_PASSWORD={{ADMIN_PASSWORD}}
- PAPERLESS_TIME_ZONE=${TZ:-Europe/Berlin}
- PAPERLESS_ADMIN_USER=${ADMIN_USER:-admin}
- PAPERLESS_ADMIN_PASSWORD=${ADMIN_PASSWORD:-change-me}
ports:
- "{{HTTP_PORT}}:8000"
- "${HTTP_PORT:-8000}:8000"
volumes:
- {{DATA_PATH}}/data:/usr/src/paperless/data
- {{DATA_PATH}}/media:/usr/src/paperless/media
- {{DATA_PATH}}/consume:/usr/src/paperless/consume
- ${DATA_PATH:-/srv/paperless}/data:/usr/src/paperless/data
- ${DATA_PATH:-/srv/paperless}/media:/usr/src/paperless/media
- ${DATA_PATH:-/srv/paperless}/consume:/usr/src/paperless/consume
@@ -0,0 +1,8 @@
{
"name": "Paperless-NGX",
"description": "Document management system that indexes scanned documents.",
"tags": [
"documents"
],
"gpu": null
}
@@ -0,0 +1,2 @@
DATA_PATH=/srv/uptime-kuma
HTTP_PORT=3001
@@ -4,6 +4,6 @@ services:
container_name: uptime-kuma
restart: unless-stopped
ports:
- "{{HTTP_PORT}}:3001"
- "${HTTP_PORT:-3001}:3001"
volumes:
- {{DATA_PATH}}:/app/data
- ${DATA_PATH:-/srv/uptime-kuma}:/app/data
@@ -0,0 +1,8 @@
{
"name": "Uptime Kuma",
"description": "Self-hosted uptime / status monitoring tool.",
"tags": [
"monitoring"
],
"gpu": null
}
@@ -0,0 +1,4 @@
TZ=Europe/Berlin
DATA_PATH=/srv/vaultwarden
HTTP_PORT=8200
ADMIN_TOKEN=change-me
@@ -4,10 +4,10 @@ services:
container_name: vaultwarden
restart: unless-stopped
environment:
- TZ={{TZ}}
- ADMIN_TOKEN={{ADMIN_TOKEN}}
- TZ=${TZ:-Europe/Berlin}
- ADMIN_TOKEN=${ADMIN_TOKEN:-change-me}
- WEBSOCKET_ENABLED=true
ports:
- "{{HTTP_PORT}}:80"
- "${HTTP_PORT:-8200}:80"
volumes:
- {{DATA_PATH}}:/data
- ${DATA_PATH:-/srv/vaultwarden}:/data
@@ -0,0 +1,9 @@
{
"name": "Vaultwarden",
"description": "Lightweight Bitwarden-compatible password manager.",
"tags": [
"password-manager",
"security"
],
"gpu": null
}