Changed how all docker images have their version set to use the compose repo

This commit is contained in:
nick committed 2026-10-26 04:38:09 +00:00
1 parent 8020f416dc
commit 0db6fdf16a
15 files changed
+643 -265

No files matched your search

+98 -68
View File
@@ -8,9 +8,12 @@ systemd:
contents: |
[Unit]
Description=Gitea MCP Server
After=docker.service
After=docker.service versions-fetch.service
Wants=versions-fetch.service
Requires=docker.service
[Service]
# Image tag comes from versions.env; see butane/versions-sync.yaml.
EnvironmentFile=/etc/versions.env
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm --force gitea-mcp
# The image ships a broken HEALTHCHECK: it is CMD-SHELL form but the
@@ -21,7 +24,7 @@ systemd:
ExecStart=/usr/bin/docker run --name gitea-mcp --no-healthcheck \
-e GITEA_HOST=https://git.atlashorizon.net \
-p 8080:8080 \
docker.gitea.com/gitea-mcp-server \
$${GITEA_MCP_IMAGE} \
/app/gitea-mcp -t http --port 8080
ExecStop=/usr/bin/docker stop gitea-mcp
Restart=always
@@ -33,9 +36,12 @@ systemd:
contents: |
[Unit]
Description=LiteLLM proxy
After=docker.service
After=docker.service versions-fetch.service
Wants=versions-fetch.service
Requires=docker.service
[Service]
# Image tag comes from versions.env; see butane/versions-sync.yaml.
EnvironmentFile=/etc/versions.env
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm --force litellm
ExecStart=/usr/bin/docker run --name litellm \
@@ -47,7 +53,7 @@ systemd:
-e JSON_LOGS=True \
-v /etc/litellm/config.yaml:/app/config.yaml:ro \
-p 4000:4000 \
docker.litellm.ai/berriai/litellm:main-stable \
$${LITELLM_IMAGE} \
--config /app/config.yaml --port 4000
ExecStop=/usr/bin/docker stop litellm
Restart=always
@@ -77,15 +83,18 @@ systemd:
contents: |
[Unit]
Description=Qdrant vector database
After=docker.service
After=docker.service versions-fetch.service
Wants=versions-fetch.service
Requires=docker.service
[Service]
# Image tag comes from versions.env; see butane/versions-sync.yaml.
EnvironmentFile=/etc/versions.env
TimeoutStartSec=0
# Both containers sit on a private docker network and Qdrant publishes
# no ports: it ships with no authentication, and nothing outside this
# host needs to reach it. Creation is idempotent, hence the "-".
ExecStartPre=-/usr/bin/docker network create openwebui
ExecStartPre=/usr/bin/docker pull qdrant/qdrant:v1.19.1
ExecStartPre=/usr/bin/docker pull $${QDRANT_IMAGE}
ExecStartPre=-/usr/bin/docker rm --force qdrant
# Vectors live on the VM disk, so a taint destroys them. That is
# deliberate -- they are derived data, rebuildable from the source
@@ -95,7 +104,7 @@ systemd:
ExecStart=/usr/bin/docker run --name qdrant \
--network openwebui \
-v /var/lib/qdrant:/qdrant/storage \
qdrant/qdrant:v1.19.1
$${QDRANT_IMAGE}
ExecStop=/usr/bin/docker stop qdrant
Restart=always
RestartSec=5s
@@ -119,12 +128,15 @@ systemd:
contents: |
[Unit]
Description=Open Terminal sandbox
After=docker.service
After=docker.service versions-fetch.service
Wants=versions-fetch.service
Requires=docker.service
[Service]
# Image tag comes from versions.env; see butane/versions-sync.yaml.
EnvironmentFile=/etc/versions.env
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker network create openwebui
ExecStartPre=/usr/bin/docker pull ghcr.io/open-webui/open-terminal:0.14.0
ExecStartPre=/usr/bin/docker pull $${OPEN_TERMINAL_IMAGE}
ExecStartPre=-/usr/bin/docker rm --force open-terminal
# A named volume, not a bind mount: /home/user belongs to the image's
# unprivileged user, and a named volume inherits that ownership from
@@ -135,7 +147,7 @@ systemd:
--network openwebui \
-v open-terminal:/home/user \
-e OPEN_TERMINAL_API_KEY=${openwebui_terminal_api_key} \
ghcr.io/open-webui/open-terminal:0.14.0
$${OPEN_TERMINAL_IMAGE}
ExecStop=/usr/bin/docker stop open-terminal
Restart=always
RestartSec=5s
@@ -148,13 +160,16 @@ systemd:
Description=Open WebUI
# After= orders the start only, it does not wait for Qdrant to be
# serving. Restart=always covers the gap if Qdrant is still coming up.
After=docker.service litellm.service qdrant.service open-terminal.service
After=docker.service litellm.service qdrant.service open-terminal.service versions-fetch.service
Wants=versions-fetch.service
Requires=docker.service
[Service]
# Image tag comes from versions.env; see butane/versions-sync.yaml.
EnvironmentFile=/etc/versions.env
TimeoutStartSec=0
# Large image, so it is pulled up front: a slow first pull is then not
# mistaken for a hung unit.
ExecStartPre=/usr/bin/docker pull ghcr.io/open-webui/open-webui:v0.11.4
ExecStartPre=/usr/bin/docker pull $${OPEN_WEBUI_IMAGE}
ExecStartPre=-/usr/bin/docker network create openwebui
ExecStartPre=-/usr/bin/docker rm --force open-webui
# No volume is mounted. Every piece of durable state lives off-box --
@@ -193,50 +208,64 @@ systemd:
-e OAUTH_PICTURE_CLAIM=avatar \
-e "OAUTH_SCOPES=openid email profile groups avatar" \
-e OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true \
-e CORS_ALLOW_ORIGIN=https://llm.atlashorizon.net \
-e WEBUI_URL=https://llm.atlashorizon.net \
-e 'TERMINAL_SERVER_CONNECTIONS=[{"id":"local","name":"Open Terminal","url":"http://open-terminal:8000","key":"${openwebui_terminal_api_key}","auth_type":"bearer","enabled":true}]' \
-p 3000:8080 \
ghcr.io/open-webui/open-webui:v0.11.4
$${OPEN_WEBUI_IMAGE}
ExecStop=/usr/bin/docker stop open-webui
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
# OpenCode headless server for n8n (and anything else on the LAN).
# OpenCode, run headless as a coding agent that n8n drives over HTTP.
#
# Prerequisites:
# - OPENCODE_SERVER_PASSWORD in Infisical (see secrets.tmpl)
# - model keys in /etc/opencode/opencode.json matching LiteLLM
# model_name values (Admin UI or GET /v1/models). Models live in
# the LiteLLM database, so this file cannot discover them.
# Prerequisites, all of which must exist before the VM is rebuilt:
# - OPENCODE_SERVER_PASSWORD in Infisical (see secrets.tmpl). Every
# Butane file is rendered on every plan, so until the secret exists
# no plan succeeds -- not just this VM's
# - each model listed in /etc/opencode/opencode.json (below) defined
# in LiteLLM under the same model_name
#
# Listens on :4096 with HTTP Basic Auth (user "opencode"). n8n on
# oswald-runner reaches it at http://192.168.7.61:4096 -- install the
# n8n-nodes-opencode-ai community package, or use HTTP Request nodes
# against the OpenAPI at /doc. LLM calls go through LiteLLM on :4000
# using the same host-IP pattern as Open WebUI.
# Unlike Open Terminal, this publishes a port: n8n runs on oswald-runner,
# so the private "openwebui" network is out of its reach. That leaves HTTP
# Basic Auth (user "opencode") as the only thing between the LAN and an
# agent with a shell, and over plain HTTP. The agent runs as root inside
# the container, with the same outbound network access as Open Terminal.
#
# From n8n: http://192.168.7.61:4096, with the n8n-nodes-opencode-ai
# community node or HTTP Request nodes (the OpenAPI spec is at /doc).
- name: opencode.service
enabled: true
contents: |
[Unit]
Description=OpenCode server
After=docker.service litellm.service
After=docker.service litellm.service versions-fetch.service
Wants=versions-fetch.service
Requires=docker.service
[Service]
# Image tag comes from versions.env; see butane/versions-sync.yaml.
EnvironmentFile=/etc/versions.env
TimeoutStartSec=0
ExecStartPre=/usr/bin/docker pull ghcr.io/anomalyco/opencode:1.18.32
ExecStartPre=/usr/bin/docker pull $${OPENCODE_IMAGE}
ExecStartPre=-/usr/bin/docker rm --force opencode
# /var/lib/opencode is the project workspace the agent edits. It
# survives container restarts; a VM taint wipes it (scratch space).
# Both directories are on the VM disk, so they survive container
# restarts but not a taint. workspace is what the agent edits; data
# holds its sessions, which n8n refers to by id across executions.
#
# The LiteLLM key is passed in the environment and read by the config
# file as {env:LITELLM_API_KEY}, which keeps the file free of secrets.
ExecStart=/usr/bin/docker run --name opencode \
-e OPENCODE_SERVER_USERNAME=opencode \
-e OPENCODE_SERVER_PASSWORD=${opencode_server_password} \
-e LITELLM_API_KEY=${litellm_master_key} \
-e OPENCODE_CONFIG=/etc/opencode/opencode.json \
-v /etc/opencode/opencode.json:/etc/opencode/opencode.json:ro \
-v /var/lib/opencode:/workspace \
-v /var/lib/opencode/workspace:/workspace \
-v /var/lib/opencode/data:/root/.local/share/opencode \
-w /workspace \
-p 4096:4096 \
ghcr.io/anomalyco/opencode:1.18.32 \
$${OPENCODE_IMAGE} \
serve --hostname 0.0.0.0 --port 4096
ExecStop=/usr/bin/docker stop opencode
Restart=always
@@ -267,15 +296,18 @@ systemd:
contents: |
[Unit]
Description=Dozzle Agent
After=docker.service
After=docker.service versions-fetch.service
Wants=versions-fetch.service
Requires=docker.service
[Service]
# Image tag comes from versions.env; see butane/versions-sync.yaml.
EnvironmentFile=/etc/versions.env
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker rm --force dozzle-agent
ExecStart=/usr/bin/docker run --name dozzle-agent \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 7007:7007 \
amir20/dozzle:v11.1.1 agent
$${DOZZLE_IMAGE} agent
ExecStop=/usr/bin/docker stop dozzle-agent
Restart=always
RestartSec=5s
@@ -289,29 +321,13 @@ storage:
# root-owned 0700 directory is what it expects.
- path: /var/lib/qdrant
mode: 0700
# OpenCode project workspace. Scratch space: survives container
# restarts, not a VM taint.
- path: /var/lib/opencode
mode: 0755
- path: /etc/opencode
# OpenCode's workspace and session store; see opencode.service. The
# image runs as root.
- path: /var/lib/opencode/workspace
mode: 0755
- path: /var/lib/opencode/data
mode: 0700
files:
# LiteLLM proxy settings. Models stay in the database
# (STORE_MODEL_IN_DB), so this file carries general_settings only --
# those have no environment equivalent and are readable only from a
# config file.
#
# Requests reach :4000 through a Cloudflare tunnel whose connector runs
# on 192.168.0.0/24, so every request arrives from that connector's
# address. Without use_x_forwarded_for LiteLLM does MCP access control
# against that peer, which sits inside the default internal ranges, and
# so treats every caller on the public internet as internal -- exposing
# MCP servers marked "available_on_public_internet: false".
#
# mcp_trusted_proxy_ranges is what keeps that safe: X-Forwarded-For is
# honoured only when the direct peer is the connector, so a crafted
# header from anywhere else cannot forge a client IP. Widen it if the
# tunnel ever moves off that subnet, or the header is ignored again.
# Companion to openwebui-reindex.service above.
#
# Note on quoting: templatefile() substitutes on raw text, so shell
@@ -356,6 +372,22 @@ storage:
echo "Qdrant empty; reindexing knowledge bases"
curl -fsS -X POST -H "Authorization: Bearer $OWUI_KEY" \
"$OWUI/api/v1/knowledge/reindex"
# LiteLLM proxy settings. Models stay in the database
# (STORE_MODEL_IN_DB), so this file carries general_settings only --
# those have no environment equivalent and are readable only from a
# config file.
#
# Requests reach :4000 through a Cloudflare tunnel whose connector runs
# on 192.168.0.0/24, so every request arrives from that connector's
# address. Without use_x_forwarded_for LiteLLM does MCP access control
# against that peer, which sits inside the default internal ranges, and
# so treats every caller on the public internet as internal -- exposing
# MCP servers marked "available_on_public_internet: false".
#
# mcp_trusted_proxy_ranges is what keeps that safe: X-Forwarded-For is
# honoured only when the direct peer is the connector, so a crafted
# header from anywhere else cannot forge a client IP. Widen it if the
# tunnel ever moves off that subnet, or the header is ignored again.
- path: /etc/litellm/config.yaml
mode: 0644
contents:
@@ -364,35 +396,33 @@ storage:
use_x_forwarded_for: true
mcp_trusted_proxy_ranges:
- 192.168.0.0/24
# OpenCode provider config. LiteLLM is an OpenAI-compatible proxy, so
# we use @ai-sdk/openai-compatible rather than a first-party provider.
# OpenCode's provider config. LiteLLM speaks the OpenAI API, so it is
# wired in through @ai-sdk/openai-compatible.
#
# Model keys under "models" must match LiteLLM model_name values exactly
# (they are stored in the LiteLLM DB, not in litellm/config.yaml). Edit
# this list to match what /v1/models returns, then taint the VM -- or
# PATCH http://192.168.7.61:4096/config at runtime. The default model
# below is the first key; change "model" when you change the set.
#
# apiKey is templated from Infisical (LITELLM_MASTER_KEY). Ignition
# snippets are plaintext on the node -- same caveat as the other units.
# Model keys must match LiteLLM model_name values exactly. Those live in
# the LiteLLM database, not in a file here, so check GET :4000/v1/models
# when changing this list. "model" is the default for new sessions.
- path: /etc/opencode/opencode.json
mode: 0600
mode: 0644
contents:
inline: |
{
"$schema": "https://opencode.ai/config.json",
"model": "litellm/claude-sonnet-4",
"model": "litellm/claude-sonnet-5",
"provider": {
"litellm": {
"npm": "@ai-sdk/openai-compatible",
"name": "LiteLLM",
"options": {
"baseURL": "http://192.168.7.61:4000/v1",
"apiKey": "${litellm_master_key}"
"apiKey": "{env:LITELLM_API_KEY}"
},
"models": {
"claude-sonnet-4": {
"name": "Claude Sonnet 4"
"claude-sonnet-5": {
"name": "Claude Sonnet 5"
},
"qwen3:4b-instruct": {
"name": "Qwen3 4B Instruct"
}
}
}