- )}
{/* Model - selector with Load button for all providers */}
diff --git a/AppImage/messages/de/common.json b/AppImage/messages/de/common.json
index a8230556..b319a539 100644
--- a/AppImage/messages/de/common.json
+++ b/AppImage/messages/de/common.json
@@ -2312,6 +2312,7 @@
"testing": "Testen…",
"testConnection": "Testverbindung",
"ollamaUrl": "Ollama-URL",
+ "ollamaApiKeyHint": "Bei einem normalen Ollama-Server leer lassen. Wenn dein Proxy oder Gateway einen Schlüssel verlangt, wird er als Authorization: Bearer gesendet. Dieses Feld aktiviert keine Authentifizierung auf dem Server.",
"getKey": "Holen Sie sich den Schlüssel",
"loadModelsPlaceholder": "Klicken Sie auf „Laden“, um Modelle abzurufen",
"selectModel": "Modell auswählen",
diff --git a/AppImage/messages/en/common.json b/AppImage/messages/en/common.json
index de39bfd6..5c4d153c 100644
--- a/AppImage/messages/en/common.json
+++ b/AppImage/messages/en/common.json
@@ -2439,6 +2439,7 @@
"testing": "Testing…",
"testConnection": "Test connection",
"ollamaUrl": "Ollama URL",
+ "ollamaApiKeyHint": "Leave empty for a standard Ollama server. If your proxy or gateway requires a key, it is sent as Authorization: Bearer. This field does not enable authentication on the server.",
"getKey": "Get key",
"loadModelsPlaceholder": "Click Load to fetch models",
"selectModel": "Select model",
diff --git a/AppImage/messages/es/common.json b/AppImage/messages/es/common.json
index 3cb1aa50..66553422 100644
--- a/AppImage/messages/es/common.json
+++ b/AppImage/messages/es/common.json
@@ -2465,6 +2465,7 @@
"testing": "Pruebas…",
"testConnection": "Conexión de prueba",
"ollamaUrl": "URL de Ollama",
+ "ollamaApiKeyHint": "Déjala vacía para un servidor Ollama estándar. Si tu proxy o pasarela requiere una clave, se envía como Authorization: Bearer. Este campo no activa la autenticación en el servidor.",
"getKey": "Obtener clave",
"loadModelsPlaceholder": "Haz clic en Cargar para buscar modelos",
"selectModel": "Seleccionar modelo",
diff --git a/AppImage/messages/fr/common.json b/AppImage/messages/fr/common.json
index 7b4aa172..621d96f2 100644
--- a/AppImage/messages/fr/common.json
+++ b/AppImage/messages/fr/common.json
@@ -2312,6 +2312,7 @@
"testing": "Essai…",
"testConnection": "Tester la connexion",
"ollamaUrl": "URL d'Ollama",
+ "ollamaApiKeyHint": "Laissez ce champ vide pour un serveur Ollama standard. Si votre proxy ou passerelle exige une clé, elle est envoyée dans Authorization: Bearer. Ce champ n'active pas l'authentification sur le serveur.",
"getKey": "Obtenir la clé",
"loadModelsPlaceholder": "Cliquez sur Charger pour récupérer les modèles",
"selectModel": "Sélectionnez le modèle",
diff --git a/AppImage/messages/it/common.json b/AppImage/messages/it/common.json
index d313e639..01c7c3d3 100644
--- a/AppImage/messages/it/common.json
+++ b/AppImage/messages/it/common.json
@@ -2312,6 +2312,7 @@
"testing": "Test in corso…",
"testConnection": "Prova connessione",
"ollamaUrl": "URL Ollama",
+ "ollamaApiKeyHint": "Lascia vuoto per un server Ollama standard. Se il proxy o gateway richiede una chiave, viene inviata come Authorization: Bearer. Questo campo non abilita l'autenticazione sul server.",
"getKey": "Ottieni chiave",
"loadModelsPlaceholder": "Fai clic su Carica per recuperare i modelli",
"selectModel": "Seleziona modello",
diff --git a/AppImage/messages/pt/common.json b/AppImage/messages/pt/common.json
index e0488b33..eb08496c 100644
--- a/AppImage/messages/pt/common.json
+++ b/AppImage/messages/pt/common.json
@@ -2312,6 +2312,7 @@
"testing": "Testando…",
"testConnection": "Conexão de teste",
"ollamaUrl": "URL de Ollama",
+ "ollamaApiKeyHint": "Deixa vazio para um servidor Ollama padrão. Se o proxy ou gateway exigir uma chave, esta é enviada como Authorization: Bearer. Este campo não ativa a autenticação no servidor.",
"getKey": "Obter chave",
"loadModelsPlaceholder": "Clique em Carregar para buscar modelos",
"selectModel": "Selecione o modelo",
diff --git a/AppImage/messages/sk/common.json b/AppImage/messages/sk/common.json
index 0842c860..6d2f0191 100644
--- a/AppImage/messages/sk/common.json
+++ b/AppImage/messages/sk/common.json
@@ -2311,6 +2311,7 @@
"testing": "Testujem…",
"testConnection": "Otestovať pripojenie",
"ollamaUrl": "URL služby Ollama",
+ "ollamaApiKeyHint": "Pre bežný server Ollama nechajte prázdne. Ak proxy alebo brána vyžaduje kľúč, odosiela sa ako Authorization: Bearer. Toto pole nezapína autentifikáciu na serveri.",
"getKey": "Získať kľúč",
"loadModelsPlaceholder": "Kliknutím na Načítať získate modely",
"selectModel": "Vyberte model",
diff --git a/AppImage/messages/sv/common.json b/AppImage/messages/sv/common.json
index 8f1833f7..f3887cad 100644
--- a/AppImage/messages/sv/common.json
+++ b/AppImage/messages/sv/common.json
@@ -2312,6 +2312,7 @@
"testing": "Testning…",
"testConnection": "Testa anslutningen",
"ollamaUrl": "Ollama URL",
+ "ollamaApiKeyHint": "Lämna tomt för en vanlig Ollama-server. Om din proxy eller gateway kräver en nyckel skickas den som Authorization: Bearer. Fältet aktiverar inte autentisering på servern.",
"getKey": "Få nyckel",
"loadModelsPlaceholder": "Klicka på Ladda för att hämta modeller",
"selectModel": "Välj modell",
diff --git a/AppImage/scripts/ai_providers/ollama_provider.py b/AppImage/scripts/ai_providers/ollama_provider.py
index de6f62ad..7b711cf3 100644
--- a/AppImage/scripts/ai_providers/ollama_provider.py
+++ b/AppImage/scripts/ai_providers/ollama_provider.py
@@ -7,6 +7,16 @@ from typing import Optional
from .base import AIProvider, AIProviderError
+def ollama_headers(api_key: str = "") -> dict:
+ """Authenticate protected Ollama gateways without requiring a key locally."""
+ if not isinstance(api_key, str) or len(api_key) > 512 or '\r' in api_key or '\n' in api_key:
+ raise AIProviderError("Invalid Ollama API key")
+ headers = {'Content-Type': 'application/json'}
+ if api_key.strip():
+ headers['Authorization'] = f'Bearer {api_key.strip()}'
+ return headers
+
+
class OllamaProvider(AIProvider):
"""Ollama provider for local AI execution."""
@@ -18,7 +28,7 @@ class OllamaProvider(AIProvider):
"""Initialize Ollama provider.
Args:
- api_key: Not used for Ollama (local execution)
+ api_key: Optional Bearer token for an authenticated Ollama gateway
model: Model name (user must select from loaded models)
base_url: Ollama server URL (default: http://localhost:11434)
"""
@@ -57,9 +67,7 @@ class OllamaProvider(AIProvider):
}
}
- headers = {
- 'Content-Type': 'application/json',
- }
+ headers = ollama_headers(self.api_key)
# Cloud models (e.g., kimi-k2.5:cloud, minimax-m2.7:cloud) need longer timeout
# because requests go through: ProxMenux -> Ollama -> Cloud Provider -> back
@@ -96,7 +104,8 @@ class OllamaProvider(AIProvider):
# First check if server is running
try:
url = f"{self.base_url.rstrip('/')}/api/tags"
- req = urllib.request.Request(url, method='GET', headers={'User-Agent': 'ProxMenux/1.0'})
+ headers = {'User-Agent': 'ProxMenux/1.0', **ollama_headers(self.api_key)}
+ req = urllib.request.Request(url, method='GET', headers=headers)
with urllib.request.urlopen(req, timeout=5) as resp:
data = json.loads(resp.read().decode('utf-8'))
@@ -122,6 +131,13 @@ class OllamaProvider(AIProvider):
'message': f"Model '{self.model}' not found. Available: {', '.join(display_models)}{'...' if len(full_model_names) > 5 else ''}",
'model': self.model
}
+ except urllib.error.HTTPError as error:
+ message = (
+ f"HTTP {error.code}: Ollama authentication failed. Check the optional API key."
+ if error.code in (401, 403)
+ else f"HTTP {error.code}: Ollama request failed"
+ )
+ return {'success': False, 'message': message, 'model': self.model}
except urllib.error.URLError:
return {
'success': False,
diff --git a/AppImage/scripts/flask_notification_routes.py b/AppImage/scripts/flask_notification_routes.py
index e5a64c5f..de011865 100644
--- a/AppImage/scripts/flask_notification_routes.py
+++ b/AppImage/scripts/flask_notification_routes.py
@@ -446,7 +446,7 @@ def get_provider_models():
Request body:
{
"provider": "gemini|groq|openai|openrouter|ollama|anthropic",
- "api_key": "your-api-key", // Not needed for ollama
+ "api_key": "your-api-key", // Optional for protected Ollama gateways
"ollama_url": "http://localhost:11434", // Only for ollama
"openai_base_url": "https://custom.endpoint/v1" // Optional for openai
}
@@ -498,9 +498,10 @@ def get_provider_models():
if provider == 'ollama':
import urllib.request
import urllib.error
+ from ai_providers.ollama_provider import ollama_headers
url = f"{ollama_url.rstrip('/')}/api/tags"
- req = urllib.request.Request(url, method='GET')
+ req = urllib.request.Request(url, method='GET', headers=ollama_headers(api_key))
req.add_header('User-Agent', 'ProxMenux-Monitor/1.1')
with urllib.request.urlopen(req, timeout=10) as resp:
diff --git a/AppImage/scripts/flask_server.py b/AppImage/scripts/flask_server.py
index 3efe9610..1f8182cb 100644
--- a/AppImage/scripts/flask_server.py
+++ b/AppImage/scripts/flask_server.py
@@ -5048,6 +5048,34 @@ def _lvm_device_args():
return lvm_device_args(_hdd_in_standby)
+def _smart_identity_payload(disk_name):
+ """The empty payload with what the host knows of the disk without sending
+ it any command: its model, its serial and whether it spins."""
+ base = _smart_default_payload()
+ try:
+ model = Path(f'/sys/block/{disk_name}/device/model').read_text().strip()
+ if model:
+ base['model'] = model
+ except OSError:
+ pass
+ try:
+ device = Path(f'/sys/block/{disk_name}/dev').read_text().strip()
+ for line in Path(f'/run/udev/data/b{device}').read_text().splitlines():
+ key, _, value = line.partition('=')
+ if key == 'E:ID_MODEL' and value.strip():
+ base['model'] = value.strip().replace('_', ' ')
+ elif key == 'E:ID_SERIAL_SHORT' and value.strip():
+ base['serial'] = value.strip()
+ except OSError:
+ pass
+ try:
+ if Path(f'/sys/block/{disk_name}/queue/rotational').read_text().strip() == '1':
+ base['rotation_rate'] = -1 # HDD, RPM unknown
+ except OSError:
+ pass
+ return base
+
+
def get_smart_data(disk_name):
"""Cached wrapper around the underlying smartctl probe.
@@ -5086,7 +5114,15 @@ def get_smart_data(disk_name):
except Exception:
policy = 'read'
if policy != 'read':
- base = dict(cached[1]) if cached else _smart_default_payload()
+ if not cached and policy == 'idle' and not _hdd_in_standby(disk_name):
+ # Nothing is known of it yet, as after a restart: a disk that is
+ # spinning is read once; a parked one is not woken.
+ result = _get_smart_data_uncached(disk_name)
+ _dth.settle_own_reads([disk_name])
+ if _smart_data_useful(result):
+ _smart_result_cache[disk_name] = (now, dict(result))
+ cached = _smart_result_cache[disk_name]
+ base = dict(cached[1]) if cached else _smart_identity_payload(disk_name)
base['temperature'] = 0
base['excluded'] = policy == 'excluded'
base['idle'] = policy == 'idle'
@@ -5098,7 +5134,7 @@ def get_smart_data(disk_name):
# the disk sleeps. The Storage view paints its own Standby badge
# from the temperature poller's flag. Don't refresh the cache
# timestamp: the moment the disk is active again we re-probe.
- base = dict(cached[1]) if cached else _smart_default_payload()
+ base = dict(cached[1]) if cached else _smart_identity_payload(disk_name)
base['standby'] = True
base['temperature'] = 0
return base
diff --git a/AppImage/scripts/notification_manager.py b/AppImage/scripts/notification_manager.py
index 52a26c2c..37fa7119 100644
--- a/AppImage/scripts/notification_manager.py
+++ b/AppImage/scripts/notification_manager.py
@@ -86,6 +86,7 @@ SENSITIVE_KEYS = {
'ai_api_key_anthropic',
'ai_api_key_openai',
'ai_api_key_openrouter',
+ 'ai_api_key_ollama',
# `telegram.bot_token` — was previously listed as `telegram.token`,
# which never matched the real config_key (`bot_token` in
# CHANNEL_TYPES). The mismatch silently sent Telegram bot tokens
@@ -3019,6 +3020,7 @@ class NotificationManager:
current_provider = self._config.get('ai_provider', 'groq')
_raw_keys = {
'groq': self._config.get('ai_api_key_groq', ''),
+ 'ollama': self._config.get('ai_api_key_ollama', ''),
'gemini': self._config.get('ai_api_key_gemini', ''),
'anthropic': self._config.get('ai_api_key_anthropic', ''),
'openai': self._config.get('ai_api_key_openai', ''),
@@ -3026,7 +3028,7 @@ class NotificationManager:
}
ai_api_keys = {
'groq': _mask_if_set(_raw_keys['groq']),
- 'ollama': '', # Ollama doesn't need API key
+ 'ollama': _mask_if_set(_raw_keys['ollama']),
'gemini': _mask_if_set(_raw_keys['gemini']),
'anthropic': _mask_if_set(_raw_keys['anthropic']),
'openai': _mask_if_set(_raw_keys['openai']),
diff --git a/AppImage/scripts/tests/test_ollama_auth.py b/AppImage/scripts/tests/test_ollama_auth.py
new file mode 100644
index 00000000..2362a096
--- /dev/null
+++ b/AppImage/scripts/tests/test_ollama_auth.py
@@ -0,0 +1,189 @@
+"""Optional Ollama gateway authentication, with no real keys or network calls."""
+
+import ast
+import io
+import json
+from pathlib import Path
+import sqlite3
+import sys
+import tempfile
+from types import SimpleNamespace
+import unittest
+from unittest import mock
+import urllib.error
+
+
+SCRIPTS = Path(__file__).resolve().parents[1]
+if str(SCRIPTS) not in sys.path:
+ sys.path.insert(0, str(SCRIPTS))
+
+from ai_providers.ollama_provider import OllamaProvider, ollama_headers
+from ai_providers.base import AIProviderError
+import notification_manager as manager_module
+
+TOKEN = "test-only-ollama-token"
+MODEL = "test-model:latest"
+
+
+def routes(manager, payload):
+ """Use the actual handlers without importing Flask or starting a server."""
+ source = SCRIPTS / "flask_notification_routes.py"
+ names = {"_resolve_masked_api_key", "get_provider_models",
+ "test_ai_connection", "reveal_notification_secret"}
+ functions = []
+ for node in ast.parse(source.read_text()).body:
+ if isinstance(node, ast.FunctionDef) and node.name in names:
+ node.decorator_list = []
+ functions.append(node)
+ scope = {
+ "__file__": str(source), "json": json,
+ "request": SimpleNamespace(get_json=lambda **kwargs: payload),
+ "jsonify": lambda value: value,
+ "notification_manager": manager,
+ "SENSITIVE_PLACEHOLDER": manager_module.SENSITIVE_PLACEHOLDER,
+ "validate_external_url": manager_module.validate_external_url,
+ "load_verified_models": lambda: {},
+ }
+ module = ast.fix_missing_locations(ast.Module(body=functions, type_ignores=[]))
+ exec(compile(module, str(source), "exec"), scope)
+ return scope
+
+
+class OllamaAuthenticationTests(unittest.TestCase):
+ def setUp(self):
+ self.requests = []
+ self.required_token = TOKEN
+ patcher = mock.patch("urllib.request.urlopen", side_effect=self.respond)
+ patcher.start()
+ self.addCleanup(patcher.stop)
+
+ def respond(self, request, timeout=None):
+ self.requests.append(request)
+ if self.required_token and request.get_header("Authorization") != "Bearer " + self.required_token:
+ raise urllib.error.HTTPError(request.full_url, 401, "Unauthorized", {},
+ io.BytesIO(b"sensitive response body"))
+ if request.full_url.endswith("/api/tags"):
+ data = {"models": [{"name": MODEL}]}
+ elif request.full_url.endswith("/api/chat"):
+ data = {"message": {"content": "CONNECTION_OK"}}
+ else:
+ raise AssertionError("Unexpected endpoint: " + request.full_url)
+ return io.BytesIO(json.dumps(data).encode())
+
+ def test_key_is_optional_and_no_authorization_header_is_sent_when_empty(self):
+ self.required_token = ""
+ for key in ("", " "):
+ provider = OllamaProvider(api_key=key, model=MODEL)
+ self.assertTrue(provider.test_connection()["success"])
+ self.assertTrue(self.requests)
+ self.assertTrue(all(r.get_header("Authorization") is None for r in self.requests))
+ self.assertFalse(OllamaProvider.REQUIRES_API_KEY)
+
+ def test_connection_authenticates_both_model_lookup_and_generation(self):
+ result = OllamaProvider(api_key=TOKEN, model=MODEL).test_connection()
+ self.assertTrue(result["success"], result)
+ self.assertEqual([r.get_method() for r in self.requests], ["GET", "POST"])
+ self.assertTrue(all(r.get_header("Authorization") == "Bearer " + TOKEN for r in self.requests))
+
+ def test_authentication_failure_is_not_reported_as_an_unreachable_server(self):
+ result = OllamaProvider(api_key="wrong-test-token", model=MODEL).test_connection()
+ self.assertFalse(result["success"])
+ self.assertIn("HTTP 401", result["message"])
+ self.assertNotIn("Cannot connect", result["message"])
+ self.assertNotIn("wrong-test-token", json.dumps(result))
+ self.assertNotIn("sensitive response", json.dumps(result))
+
+ def test_invalid_header_values_are_rejected_without_echoing_them(self):
+ for value in ("test-secret\r\nInjected: header", "x" * 513, None):
+ with self.subTest(value_type=type(value).__name__):
+ with self.assertRaisesRegex(AIProviderError, "^Invalid Ollama API key$"):
+ ollama_headers(value)
+ self.assertEqual(self.requests, [])
+
+ def test_model_loading_uses_the_saved_key_when_the_browser_sends_a_mask(self):
+ manager = SimpleNamespace(_config={"ai_api_key_ollama": TOKEN})
+ handlers = routes(manager, {"provider": "ollama", "api_key": manager_module.SENSITIVE_PLACEHOLDER,
+ "ollama_url": "http://localhost:11434"})
+ result = handlers["get_provider_models"]()
+ self.assertTrue(result["success"], result)
+ self.assertEqual(result["models"], [MODEL])
+ self.assertEqual(self.requests[0].get_header("Authorization"), "Bearer " + TOKEN)
+ self.assertNotIn(TOKEN, json.dumps(result))
+
+ def test_model_loading_without_a_key_still_works_for_native_ollama(self):
+ self.required_token = ""
+ handlers = routes(SimpleNamespace(_config={}), {
+ "provider": "ollama", "ollama_url": "http://localhost:11434"})
+ result = handlers["get_provider_models"]()
+ self.assertTrue(result["success"], result)
+ self.assertIsNone(self.requests[0].get_header("Authorization"))
+
+ def test_test_ai_route_reuses_the_saved_key(self):
+ handlers = routes(SimpleNamespace(_config={"ai_api_key_ollama": TOKEN}), {
+ "provider": "ollama", "model": MODEL,
+ "api_key": manager_module.SENSITIVE_PLACEHOLDER,
+ "ollama_url": "http://localhost:11434"})
+ result = handlers["test_ai_connection"]()
+ self.assertTrue(result["success"], result)
+ self.assertTrue(all(r.get_header("Authorization") == "Bearer " + TOKEN for r in self.requests))
+
+ def test_reveal_endpoint_recognizes_the_ollama_secret(self):
+ handlers = routes(SimpleNamespace(_config={"ai_api_key_ollama": TOKEN}), {"ai_provider": "ollama"})
+ self.assertEqual(handlers["reveal_notification_secret"](), ({"value": TOKEN}, 200))
+
+ def test_real_notification_provider_receives_the_ollama_key(self):
+ manager = manager_module.NotificationManager()
+ manager._config = {"ai_enabled": "true", "ai_provider": "ollama",
+ "ai_api_key_ollama": TOKEN, "ai_model_ollama": MODEL,
+ "ai_ollama_url": "http://localhost:11434"}
+ from notification_templates import AIEnhancer
+ rewriter = AIEnhancer(manager._build_ai_config())
+ self.assertIsInstance(rewriter._provider, OllamaProvider)
+ self.assertEqual(rewriter._provider.generate("system", "user"), "CONNECTION_OK")
+ self.assertEqual(self.requests[0].get_header("Authorization"), "Bearer " + TOKEN)
+
+
+class OllamaKeyStorageTests(unittest.TestCase):
+ def setUp(self):
+ directory = tempfile.TemporaryDirectory()
+ self.addCleanup(directory.cleanup)
+ self.db = Path(directory.name) / "settings.db"
+ with sqlite3.connect(str(self.db)) as conn:
+ conn.execute("CREATE TABLE user_settings (setting_key TEXT PRIMARY KEY, setting_value TEXT, updated_at TEXT)")
+ for name, value in (("DB_PATH", self.db), ("ENCRYPTION_KEY_FILE", Path(directory.name) / "key")):
+ patcher = mock.patch.object(manager_module, name, value)
+ patcher.start()
+ self.addCleanup(patcher.stop)
+ self.manager = manager_module.NotificationManager()
+ self.manager._rebuild_channels = mock.Mock()
+
+ def stored_value(self):
+ with sqlite3.connect(str(self.db)) as conn:
+ return conn.execute("SELECT setting_value FROM user_settings WHERE setting_key = ?",
+ ("notification.ai_api_key_ollama",)).fetchone()[0]
+
+ def test_key_is_encrypted_masked_and_loaded_for_runtime_use(self):
+ result = self.manager.save_settings({"ai_provider": "ollama", "ai_api_key_ollama": TOKEN})
+ self.assertTrue(result["success"], result)
+ stored = self.stored_value()
+ self.assertTrue(stored.startswith("ENC2:"))
+ self.assertNotIn(TOKEN, stored)
+ self.assertEqual(manager_module.decrypt_sensitive_value(stored), TOKEN)
+ self.assertEqual(self.manager.get_settings()["config"]["ai_api_keys"]["ollama"],
+ manager_module.SENSITIVE_PLACEHOLDER)
+ fresh = manager_module.NotificationManager()
+ fresh._load_config()
+ self.assertEqual(fresh._build_ai_config()["ai_api_key"], TOKEN)
+
+ def test_masked_round_trip_keeps_the_key_and_an_empty_value_removes_it(self):
+ self.assertTrue(self.manager.save_settings({"ai_api_key_ollama": TOKEN})["success"])
+ stored = self.stored_value()
+ self.assertTrue(self.manager.save_settings({"ai_api_key_ollama": manager_module.SENSITIVE_PLACEHOLDER})["success"])
+ self.assertEqual(self.stored_value(), stored)
+ self.assertTrue(self.manager.save_settings({"ai_api_key_ollama": ""})["success"])
+ self.assertEqual(self.stored_value(), "")
+ self.assertEqual(self.manager.get_settings()["config"]["ai_api_keys"]["ollama"], "")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/lang/de.json b/lang/de.json
index 94e8e43e..8a299f33 100644
--- a/lang/de.json
+++ b/lang/de.json
@@ -47,6 +47,7 @@
"A GPS tracking platform with device management and fleet reports.": "Eine GPS-Tracking-Plattform mit Geräteverwaltung und Flottenberichten.",
"A GTK Broadway web UI for libvirt and virt-manager.": "Ein GTK Broadway Web UI für libvirt und virt-manager.",
"A Personal Relationship Management tool to help you document your social life.": "Eine persönliche Beziehung Management-Tool, das Ihnen hilft, Ihr soziales Leben zu dokumentieren.",
+ "A REST backend for restic backups; it has no web interface.": "Ein REST-Backend für restic-Sicherungen ohne Weboberfläche.",
"A VirtIO ISO already exists. Do you want to overwrite it?": "Eine VirtIO-ISO ist bereits vorhanden. Möchten Sie es überschreiben?",
"A ZFS pool with this name already exists.": "Ein ZFS-Pool mit diesem Namen ist bereits vorhanden.",
"A ZFS pool with this name already exists:": "Ein ZFS-Pool mit diesem Namen existiert bereits:",
@@ -94,6 +95,7 @@
"A lightweight dashboard that brings feeds, services and useful widgets together.": "Ein leichtgewichtiges Dashboard, das Feeds, Dienste und nützliche Widgets bündelt.",
"A lightweight hub for monitoring servers and containers.": "Ein leichtgewichtiger Hub zur Überwachung von Servern und Containern.",
"A lightweight self-hosted Kanban board.": "Ein leichtgewichtiges, selbst gehostetes Kanban-Board.",
+ "A lightweight self-hosted URL shortener with SQLite storage.": "Ein schlanker, selbst gehosteter URL-Kürzer mit SQLite-Speicher.",
"A lightweight self-hosted music server with Subsonic API support.": "Ein leichtgewichtiger, selbst gehosteter Musikserver mit Unterstützung für die Subsonic-API.",
"A lightweight self-hosted pastebin for text and files.": "Ein leichtgewichtiger, selbst gehosteter Pastebin-Dienst zum Teilen von Texten und Dateien.",
"A lightweight static homepage for organizing self-hosted services.": "Eine leichtgewichtige, statische Startseite zum Organisieren selbst gehosteter Dienste.",
@@ -124,6 +126,7 @@
"A pending operation exists for": "Eine ausstehende operation existiert für",
"A pending stack assembly already exists; it is not overwritten": "Eine ausstehende Stapelanordnung existiert bereits; sie wird nicht überschrieben",
"A personal homepage for your apps and dashboards.": "Eine persönliche Startseite für deine Apps und Dashboards.",
+ "A personal, self-hosted microblog and timeline.": "Ein persönlicher, selbst gehosteter Microblog mit Chronik.",
"A previous NVIDIA refresh is pending review": "Eine frühere NVIDIA-Aktualisierung steht noch aus",
"A previous VFIO passthrough configuration was detected for the following NVIDIA GPU(s):": "Für die folgenden NVIDIA-GPU(s) wurde eine frühere VFIO-Passthrough-Konfiguration erkannt:",
"A privacy-first, open-source platform for knowledge management and collaboration.": "Eine Privacy-First-Open-Source-Plattform für Wissensmanagement und Zusammenarbeit.",
@@ -147,15 +150,18 @@
"A self-hosted Q&A and knowledge-sharing platform.": "Eine selbst gehostete Plattform für Fragen und Antworten sowie den Wissensaustausch.",
"A self-hosted RSS reader inspired by Google Reader.": "Selbst gehosteter RSS-Reader, inspiriert von Google Reader.",
"A self-hosted app for organizing recurring tasks and household chores, with data stored in SQLite.": "Selbst gehostete App zum Organisieren wiederkehrender Aufgaben und Haushaltsarbeiten, mit Speicherung in SQLite.",
+ "A self-hosted app for organizing tabletop role-playing campaigns.": "Eine selbst gehostete App zum Organisieren von Pen-and-Paper-Rollenspielkampagnen.",
"A self-hosted bookmark manager and web archiver.": "Ein selbst gehosteter Lesezeichenmanager und Webarchiv.",
"A self-hosted bookmark manager.": "Selbst gehosteter Lesezeichenmanager.",
"A self-hosted file sharing service with expiring downloads.": "Selbst gehosteter Dateifreigabedienst mit zeitlich begrenzten Downloads.",
"A self-hosted link-in-bio page.": "Selbst gehostete Link-in-Bio-Seite.",
"A self-hosted read-it-later application.": "Eine selbst gehostete App zum Speichern von Artikeln für später.",
"A self-hosted task manager for planning work and tracking time.": "Ein selbst gehosteter Aufgabenmanager zur Arbeitsplanung und Zeiterfassung.",
+ "A self-hosted tracker for habits, tasks, notes and goals.": "Ein selbst gehosteter Tracker für Gewohnheiten, Aufgaben, Notizen und Ziele.",
"A self-hosted vehicle maintenance and fuel log.": "Ein selbst gehostetes Protokoll für Fahrzeugwartung und Kraftstoffverbrauch.",
"A self-hosted workflow scheduler with a web interface.": "Selbst gehosteter Workflow-Scheduler mit Weboberfläche.",
"A self-hosted, goal-free habit tracking tool.": "Ein selbst gehostetes, zielfreies Habit-Tracking-Tool.",
+ "A self-hosted, no-code database and application platform.": "Selbst gehostete No-Code-Plattform für Datenbanken und Anwendungen.",
"A self-improving AI agent with memory, skills, messaging, and a web dashboard.": "Ein sich selbst verbessernder KI-Agent mit Speicher, Fähigkeiten, Messaging und einem Web-Dashboard.",
"A server reboot is recommended for all changes to take full effect.": "Damit alle Änderungen vollständig wirksam werden, wird ein Neustart des Servers empfohlen.",
"A shared directory was replaced during the installation": "Ein gemeinsames Verzeichnis wurde während der Installation ersetzt",
@@ -276,6 +282,7 @@
"Active ZRAM algorithm, capacity or swap sources did not match the profile": "Aktiver ZRAM-Algorithmus, Kapazität oder Swap-Quellen stimmen nicht mit dem Profil überein",
"Active exports:": "Aktive Exporte:",
"Active session:": "Aktive Sitzung:",
+ "Activepieces": "Activepieces",
"Actual device path on the host": "Tatsächlicher Gerätepfad auf dem Host",
"Adaptation file too large": "Anpassungsdatei zu groß",
"Adaptation file with unexpected permissions or owner": "Anpassungsdatei mit unerwarteten Berechtigungen oder Besitzer",
@@ -783,6 +790,8 @@
"Build and install the gasket and apex kernel modules (DKMS)": "gasket- und apex-Kernelmodule erstellen und installieren (DKMS)",
"Build dependencies installed.": "Build-Abhängigkeiten installiert.",
"Build dynamic websites using SQL files.": "Dynamische Websites mit SQL-Dateien erstellen.",
+ "Build reports and dashboards from your data.": "Erstellen Sie Berichte und Dashboards aus Ihren Daten.",
+ "Build visual automations for personal and hobby use.": "Erstellen Sie visuelle Automatisierungen für private Zwecke und Hobbys.",
"Build your personal knowledge base with TriliumNext Notes": "Erstellen Sie Ihre persönliche Wissensbasis mit TriliumNext Notes",
"Bulk generation requires GITHUB_TOKEN to obtain an immutable revision per application": "Die Massengenerierung erfordert GITHUB_TOKEN, um für jede Anwendung eine unveränderliche Revision zu erhalten",
"Business & ERP": "Unternehmen und ERP",
@@ -827,6 +836,7 @@
"Calibre-web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. It is also possible to integrate google drive and edit metadata and your calibre library through the app itself.": "Calibre-web ist eine Web-App, die eine saubere Schnittstelle zum Durchsuchen, Lesen und Herunterladen von eBooks mit einer vorhandenen Calibre-Datenbank bietet. Es ist auch möglich, Google Drive zu integrieren und Metadaten und Ihre calibre-Bibliothek über die App selbst zu bearbeiten.",
"Calligra is an office and graphic art suite by KDE. It is available for desktop PCs, tablet computers, and smartphones. It contains applications for word processing, spreadsheets, presentation, vector graphics, and editing databases.": "Calligra ist eine Büro- und Grafiksuite von KDE. Es ist für Desktop-PCs, Tablet-Computer und Smartphones verfügbar. Es enthält Anwendungen für Textverarbeitung, Tabellenkalkulationen, Präsentation, Vektorgrafiken und Bearbeitungsdatenbanken.",
"Calnode": "Calnode",
+ "Campfire first-run setup": "Ersteinrichtung von Campfire",
"Cancel": "Stornieren",
"Cancel restore": "Wiederherstellung abbrechen",
"Cancel this setup": "Brechen Sie diese Einrichtung ab",
@@ -974,6 +984,8 @@
"Checklist post-upgrade finished. Warnings:": "Checkliste nach dem Upgrade abgeschlossen. Warnungen:",
"Checklist pre-check finished. Warnings:": "Checklisten-Vorprüfung abgeschlossen. Warnungen:",
"Checks for LVM and storage issues": "Prüft auf LVM- und Speicherprobleme",
+ "Chhoto URL": "Chhoto URL",
+ "Chhoto URL administrator password": "Administratorkennwort für Chhoto URL",
"Choose": "Auswählen",
"Choose BIOS Type": "BIOS-Typ auswählen",
"Choose BIOS type": "Wählen Sie den BIOS-Typ",
@@ -1192,6 +1204,7 @@
"Configured during installation": "Bei der Installation konfiguriert",
"Configured import disk %s as %s": "Importierter Datenträger %s als %s konfiguriert",
"Configured loader disk as ide0": "Loader-Datenträger als ide0 konfiguriert",
+ "Configured username": "Konfigurierter Benutzername",
"Configured virtual disk as %s, %sGB on %s": "Virtueller Datenträger als %s konfiguriert, %sGB auf %s",
"Configuring AMD GPU passthrough...": "AMD-GPU-Passthrough wird konfiguriert...",
"Configuring AMD softdep...": "Konfigurieren der AMD-Softdep...",
@@ -1601,6 +1614,7 @@
"Create and stream custom live TV channels from your media library.": "Erstelle eigene Live-TV-Kanäle aus deiner Medienbibliothek und streame sie.",
"Create credentials file (recommended):": "Anmeldeinformationsdatei erstellen (empfohlen):",
"Create directory": "Verzeichnis erstellen",
+ "Create encrypted, deduplicated backups and restore files.": "Erstellen Sie verschlüsselte, deduplizierte Sicherungen und stellen Sie Dateien wieder her.",
"Create export directory:": "Exportverzeichnis erstellen:",
"Create mount point:": "Mountpunkt erstellen:",
"Create new directory in /mnt": "Erstellen Sie ein neues Verzeichnis in /mnt",
@@ -1611,9 +1625,11 @@
"Create share directory:": "Freigabeverzeichnis erstellen:",
"Create shared directory:": "Freigegebenes Verzeichnis erstellen:",
"Create this LXC in privileged mode?": "Erstellen Sie diese LXC im privilegierten Modus?",
+ "Create with create_user in the container console": "Erstellen Sie den Benutzer mit create_user in der Container-Konsole",
"Create, send and sign documents with a self-hosted web app.": "Erstelle, versende und signiere Dokumente mit einer selbst gehosteten Web-App.",
"Created common remapped user": "Allgemeiner neu zugeordneter Benutzer erstellt",
"Created directory on host:": "Erstelltes Verzeichnis auf Host:",
+ "Created during first-run setup": "Wird bei der Ersteinrichtung erstellt",
"Created persistent names for": "Persistente Namen erstellt für",
"Created remapped user": "Neu zugeordneter Benutzer erstellt",
"Created sharedfiles group (GID: 101000)": "Sharedfiles-Gruppe erstellt (GID: 101000)",
@@ -2127,6 +2143,7 @@
"Each LUN will appear as a block device assignable to VMs.": "Jede LUN wird als Blockgerät angezeigt, das VMs zugewiesen werden kann.",
"Each peer gets its configuration and its QR code inside the container: /config/peer1/peer1.conf and /config/peer1/peer1.png, or /config/peer_/peer_.conf when names were given.": "Jeder Peer erhält seine Konfiguration und seinen QR-Code im Container: /config/peer1/peer1.conf und /config/peer1/peer1.png oder /config/peer /peer .conf, wenn Namen angegeben wurden.",
"Ebook and audiobook collection manager for Usenet and BitTorrent users.": "Ebook und audiobook collection manager für usenet- und bittorrent-benutzer.",
+ "Ech0": "Ech0",
"Eden is an experimental open-source emulator for the Nintendo Switch, built with performance and stability in mind.": "Eden ist ein experimenteller Open-Source-Emulator für den Nintendo Switch, der auf Leistung und Stabilität ausgerichtet ist.",
"Edge TPU runtime installed.": "Edge TPU-Laufzeit installiert.",
"Edit": "Bearbeiten",
@@ -2136,6 +2153,7 @@
"Edit the VM machine type to q35 and try again.": "Bearbeiten Sie den VM-Maschinentyp auf q35 und versuchen Sie es erneut.",
"Edition:": "Edition:",
"Email address for certificate expiry notices (required by ZeroSSL)": "E-Mail-Adresse für Zertifikatsverfallsmeldungen (required by ZeroSSL)",
+ "Email address for the OpenHabitTracker account": "E-Mail-Adresse für das OpenHabitTracker-Konto",
"Email address of the LibreDB Studio administrator": "E-Mail-Adresse des LibreDB Studio-Administrators",
"Email address of the NetBox admin account": "E-Mail-Adresse des NetBox Administratorkontos",
"Email address of the admin account": "E-Mail-Adresse des Administratorkontos",
@@ -2299,6 +2317,7 @@
"Enter the BORG REPOKEY passphrase for target:": "Geben Sie die BORG REPOKEY-Passphrase für das Ziel ein:",
"Enter the Samba username:": "Geben Sie den Samba-Benutzernamen ein:",
"Enter the VMID for the new VM:": "Geben Sie die VMID für die neue VM ein:",
+ "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Geben Sie die Basis-URL ein, über die Clients und Webhook-Anbieter diese Activepieces-Instanz erreichen.",
"Enter the container terminal:": "Betreten Sie das Containerterminal:",
"Enter the full path to the directory containing disk images:": "Geben Sie den vollständigen Pfad zum Verzeichnis mit den Disk-Images ein:",
"Enter the full path to the host directory:": "Geben Sie den vollständigen Pfad zum Hostverzeichnis ein:",
@@ -2668,7 +2687,9 @@
"Firewall allows port": "Firewall erlaubt Port",
"Firewall settings": "Firewall-Einstellungen",
"Firmware :": "Firmware:",
+ "First Ech0 account (becomes Owner)": "Erstes Ech0-Konto (wird zum Eigentümer)",
"First Jelu administrator": "Erster Jelu-Administrator",
+ "First Watcharr account (becomes administrator)": "Erstes Watcharr-Konto (wird zum Administrator)",
"First complete DSM setup and verify Web UI/SSH access.": "Schließen Sie zunächst die DSM-Einrichtung ab und überprüfen Sie den Web-UI-/SSH-Zugriff.",
"First complete ZimaOS setup and verify remote access (web/SSH).": "Schließen Sie zunächst das ZimaOS-Setup ab und überprüfen Sie den Fernzugriff (Web/SSH).",
"First install the GPU drivers inside the guest and verify remote access (RDP/SSH).": "Installieren Sie zunächst die GPU-Treiber im Gast und überprüfen Sie den Remotezugriff (RDP/SSH).",
@@ -3306,6 +3327,7 @@
"Initial Redmine administrator": "Erster Redmine-Administrator",
"Initial Stirling-PDF account": "Erstes Stirling-PDF-Konto",
"Initial Tandoor administrator": "Initial Tandoor Administrator",
+ "Initial Umami administrator": "Erster Administrator für Umami",
"Initial administrator created:": "Erster Administrator erstellt:",
"Initial administrator user": "Erster Administrator",
"Initializing Borg repository if needed...": "Initialisierung des Borg-Repositorys bei Bedarf ...",
@@ -3404,6 +3426,7 @@
"Installing Fail2Ban...": "Fail2Ban wird installiert...",
"Installing Git as a prerequisite...": "Git als Voraussetzung installieren...",
"Installing Intel VA-API drivers in container...": "Intel VA-API-Treiber werden im Container installiert...",
+ "Installing Log2RAM": "Log2RAM wird installiert",
"Installing Log2RAM from GitHub...": "Log2RAM von GitHub installieren...",
"Installing Log2RAM from source...": "Log2RAM von der Quelle installieren...",
"Installing NFS Client in LXC": "NFS-Client in LXC installieren",
@@ -3753,6 +3776,8 @@
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa ist ein leistungsstarkes Tool, das Ihnen die vollständige Kontrolle über Ihre Medienbibliotheken gibt. Mit Kometa können Sie Ihre Anpassung auf die nächste Ebene bringen, mit granularer Kontrolle über Metadaten, Sammlungen, Overlays und vielem mehr.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa liest seine Konfiguration aus /config/config.yml und der Container liefert nur /config/config.yml.template. Kopieren Sie die Vorlage auf config.yml, füllen Sie die required Plex und TMDb-Verbindungen aus und starten Sie dann den Container neu.",
"Komga is a media server for your comics, mangas, BDs, magazines and eBooks.": "Komga ist ein Medienserver für Ihre Comics, Mangas, BDs, Magazine und eBooks.",
+ "Kopia Web UI": "Kopia-Weboberfläche",
+ "Kopia Web UI login": "Anmeldung an der Kopia-Weboberfläche",
"Krita is a professional FREE and open source painting program. It is made by artists that want to see affordable art tools for everyone.": "Krita ist ein professionelles kostenloses und Open-Source-Malprogramm. Es wird von Künstlern gemacht, die erschwingliche Kunstwerkzeuge für alle sehen wollen.",
"LAN access to the kept containers (stack configuration incomplete):": "LAN-Zugriff auf die aufbewahrten Container (Stack-Konfiguration unvollständig):",
"LAN address applied to the application URLs": "LAN-Adresse für die Anwendungs-URLs",
@@ -3894,10 +3919,14 @@
"Log aggregation server for Grafana.": "Protokollaggregationsserver für Grafana.",
"Log file": "Protokolldatei",
"Log2RAM already registered — updating to latest configuration": "Log2RAM bereits registriert – Aktualisierung auf die neueste Konfiguration",
+ "Log2RAM could not be downloaded": "Log2RAM konnte nicht heruntergeladen werden",
"Log2RAM installation and configuration completed successfully.": "Die Installation und Konfiguration von Log2RAM wurde erfolgreich abgeschlossen.",
"Log2RAM installation cancelled by user": "Die Log2RAM-Installation wurde vom Benutzer abgebrochen",
"Log2RAM installation verification failed. Check /tmp/log2ram_install.log": "Die Überprüfung der Log2RAM-Installation ist fehlgeschlagen. Überprüfen Sie /tmp/log2ram_install.log",
"Log2RAM installed successfully": "Log2RAM erfolgreich installiert",
+ "Log2RAM installed:": "Log2RAM installiert:",
+ "Log2RAM is removed at the next reboot, after writing /var/log to disk": "Log2RAM wird beim nächsten Neustart entfernt, nachdem /var/log auf die Festplatte geschrieben wurde",
+ "Log2RAM keeps /var/log in RAM; the journal stays on disk from the next boot": "Log2RAM hält /var/log im RAM; das Journal bleibt ab dem nächsten Start auf der Festplatte",
"Log2RAM keeps the journal in order from the next reboot": "Log2RAM hält das Journal ab dem nächsten Neustart in Ordnung",
"Log2RAM size set to:": "Log2RAM-Größe eingestellt auf:",
"Log2RAM write scheduled every": "Log2RAM-Schreibvorgang alle geplant",
@@ -3980,6 +4009,7 @@
"Manage custom backup paths": "Benutzerdefinierte Sicherungspfade verwalten",
"Manage custom paths (add / remove your folders)": "Benutzerdefinierte Pfade verwalten (Ordner hinzufügen/entfernen)",
"Manage data and APIs with a modern visual interface.": "Daten und APIs über eine moderne visuelle Oberfläche verwalten.",
+ "Manage database changes, access and workflows.": "Verwalten Sie Datenbankänderungen, Zugriffsrechte und Workflows.",
"Manage installed OCI applications": "Verwalten installierter OCI-Anwendungen",
"Manage local backup target": "Lokales Backup-Ziel verwalten",
"Manage private image registries.\nSaved credentials let ProxMenux access your private images.": "Private Bildregister verwalten.\nGespeicherte Anmeldeinformationen ermöglichen ProxMenux den Zugriff auf Ihre privaten Bilder.",
@@ -4847,6 +4877,9 @@
"Open-source in-memory data store.": "Open-Source-Datenspeicher im Arbeitsspeicher.",
"Open-source self-hosted SQL IDE.": "Open-Source selbst gehostete SQL IDE.",
"OpenClaw is a personal AI assistant you run on your own devices": "OpenClaw ist ein persönlicher KI-Assistent, den Sie auf Ihren eigenen Geräten ausführen",
+ "OpenHabitTracker": "OpenHabitTracker",
+ "OpenHabitTracker account": "OpenHabitTracker-Konto",
+ "OpenHabitTracker initial username": "Erster OpenHabitTracker-Benutzername",
"OpenList": "OpenList",
"OpenObserve administrator": "OpenObserve-Administrator",
"OpenShot Video Editor is an award-winning free and open-source video editor for Linux, Mac, and Windows, and is dedicated to delivering high quality video editing and animation solutions to the world.": "OpenShot Video Editor ist ein preisgekrönter kostenloser und Open-Source-Video-Editor für Linux, Mac und Windows und widmet sich der Bereitstellung hochwertiger Videobearbeitungs- und Animationslösungen für die Welt.",
@@ -5214,6 +5247,7 @@
"Primary upstream DNS server": "Primärer Upstream-DNS-Server",
"Privacy-first finance app with envelope budgeting and multi-device sync.": "Privacy-First Finance App mit Umschlag budgeting und Multi-Device-Sync.",
"Privacy-first, self-hosted PDF toolkit": "Privacy-First, selbst gehostetes PDF-Toolkit",
+ "Privacy-focused, self-hosted web analytics.": "Datenschutzorientierte, selbst gehostete Webanalyse.",
"Private installation record saved": "Private Installationsaufzeichnung gespeichert",
"Private network assigned automatically:": "Privates Netzwerk automatisch zugewiesen:",
"Private network kept, because other containers still use it:": "Privates Netzwerk beibehalten, da es weiterhin von anderen Containern verwendet wird:",
@@ -5337,6 +5371,7 @@
"Public UDP port clients connect to": "Öffentliche UDP Port Clients verbinden sich mit",
"Public UDP port peers connect to": "Öffentliche UDP-Port Peers verbinden sich mit",
"Public URL of phpMyAdmin when it is served behind a reverse proxy": "Öffentliche URL von phpMyAdmin, wenn sie hinter einem Reverse-Proxy bereitgestellt wird",
+ "Public URL used to access Baserow (for example https://baserow.example.com)": "Öffentliche URL für den Zugriff auf Baserow (zum Beispiel https://baserow.example.com)",
"Public address clients connect to (vpn.example.com or a public IP)": "Öffentliche Adress-Clients verbinden sich mit (vpn.example.com oder eine öffentliche IP)",
"Public address peers connect to, or auto to detect it (vpn.example.com)": "Public Adress Peers verbinden, oder Auto, um es zu erkennen (vpn.example.com)",
"Publish each directory from Resources: open it, switch on SMB, NFS or both, and save.": "Veröffentlichen Sie jedes Verzeichnis über Ressourcen: Öffnen Sie es, schalten Sie SMB, NFS oder beide ein und speichern Sie es.",
@@ -5362,6 +5397,7 @@
"RAM in MiB": "RAM in MiB",
"REPAIR SUMMARY": "REPARATURÜBERSICHT",
"REQUIREMENTS:": "ANFORDERUNGEN:",
+ "REST API Basic Authentication user": "Benutzer für die HTTP-Basisauthentifizierung der REST-API",
"ROCm does not support this GPU officially. The application can use it by presenting it as another generation of its family: it is faster than the CPU, but under load the GPU can stop responding, and then the application does not answer until its container is restarted. The ROCm image is also larger than the others.": "ROCm unterstützt diese GPU offiziell nicht. Die Anwendung kann sie verwenden, indem sie sie als eine andere Generation ihrer Familie ausgibt: Sie ist schneller als die CPU, aber unter Last kann die GPU aufhören zu reagieren, und dann antwortet die Anwendung erst wieder, wenn ihr Container neu gestartet wird. Das ROCm-Image ist außerdem größer als die anderen.",
"ROCm requires /dev/kfd on the host": "ROCm erfordert /dev/kfd auf dem Host",
"ROCm requires the render device of an AMD GPU": "ROCm erfordert das Rendergerät eines AMD GPU",
@@ -5672,6 +5708,7 @@
"Repositories switched to no-subscription": "Repositorys wurden auf „Kein Abonnement“ umgestellt",
"Repository check failed. NVIDIA installation stopped.": "Repository-Überprüfung fehlgeschlagen. Die Installation von NVIDIA wurde gestoppt.",
"Repository check failed. Update stopped.": "Repository-Überprüfung fehlgeschlagen. Update gestoppt.",
+ "Repository encryption password": "Kennwort zur Verschlüsselung des Repositorys",
"Repository helper unavailable. NVIDIA installation stopped.": "Repository-Helfer nicht verfügbar. Die Installation von NVIDIA wurde gestoppt.",
"Repository policy returned an unexpected result.": "Die Repository-Richtlinie hat ein unerwartetes Ergebnis zurückgegeben.",
"Repository ready.": "Repository bereit.",
@@ -5789,6 +5826,7 @@
"Root shell/profile config": "Root-Shell-/Profilkonfiguration",
"Root user on the PVE host (default 'root'):": "Root-Benutzer auf dem PVE-Host (Standard „root“):",
"Rootfs size in GB": "Rootfs Größe in GB",
+ "Rotate and write /var/log to disk when it passes 85% of its size?": "/var/log rotieren und auf die Festplatte schreiben, wenn es 85 % seiner Größe überschreitet?",
"Rotate the recovery passphrase": "Drehen Sie die Wiederherstellungspassphrase",
"Routing Information": "Routing-Informationen",
"Routing Table": "Routing-Tabelle",
@@ -6261,6 +6299,7 @@
"Set ownership and permissions:": "Besitz und Berechtigungen festlegen:",
"Set the initial Technitium DNS administrator password": "Initiales Administrationspasswort für Technitium DNS festlegen",
"Set this disk as the primary boot disk?": "Diese Festplatte als primäre Startfestplatte festlegen?",
+ "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Setzen Sie den Wert nur in einem privaten LAN mit ausschließlich HTTP auf 1; andernfalls verwenden Sie HTTPS.",
"Set up custom MOTD banner": "Richten Sie ein benutzerdefiniertes MOTD-Banner ein",
"Set up the apex group and udev rules": "Richten Sie die Apex-Gruppe und die udev-Regeln ein",
"Set up time synchronization and entropy generation": "Richten Sie Zeitsynchronisation und Entropieerzeugung ein",
@@ -6966,6 +7005,7 @@
"The domain must resolve to the public address of this network before the certificate can be issued.": "Die Domain muss sich auf die öffentliche Adresse dieses Netzwerks auflösen, bevor das Zertifikat ausgestellt werden kann.",
"The download client still needs to be configured.": "Der Download-Client muss noch konfiguriert werden.",
"The download stopped progressing; cancelling this attempt.": "Der Download hat aufgehört voranzukommen; diesen Versuch abzubrechen.",
+ "The downloaded Log2RAM does not match the expected release": "Das heruntergeladene Log2RAM entspricht nicht der erwarteten Version",
"The downloaded image does not match its manifest": "Das heruntergeladene Bild stimmt nicht mit seinem Manifest überein",
"The downloaded image is corrupt:": "Das heruntergeladene Bild ist beschädigt:",
"The dpkg package database is clean.": "Die dpkg-Paketdatenbank ist sauber.",
@@ -7115,6 +7155,7 @@
"The oci-compose is incomplete: it does not say which image to install.": "Das oci-compose ist unvollständig: Es gibt nicht an, welches Image installiert werden soll.",
"The oci-compose is not valid JSON:": "Das oci-compose ist kein gültiges JSON:",
"The official Tandoor startup executable is missing": "Die offizielle Tandoor Startup Executable fehlt",
+ "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Die offiziellen Compose-Dienste von Umami und PostgreSQL fordern weder den privilegierten Modus noch eine gelockerte Isolation des Hostsystems an.",
"The official inventory contains no NVIDIA devices": "Das offizielle Inventar enthält keine NVIDIA Geräte",
"The official inventory contains no NVIDIA driver components": "Das offizielle Inventar enthält keine NVIDIA Treiberkomponenten",
"The official startup cannot be reproduced": "Das offizielle Startup kann nicht reproduziert werden",
@@ -7535,6 +7576,7 @@
"Total routes": "Gesamtrouten",
"Total size:": "Gesamtgröße:",
"Toxiproxy": "Toxiproxy",
+ "Track movies, television, anime and games you want to enjoy.": "Behalten Sie Filme, Serien, Anime und Spiele im Blick, die Sie ansehen oder spielen möchten.",
"Track subscriptions and recurring expenses.": "Abonnements und wiederkehrende Ausgaben verwalten.",
"Track your music listening history on your own server.": "Hörverlauf für Musik auf dem eigenen Server erfassen.",
"Track your reading, books and history in a private library.": "Leseaktivitäten, Bücher und Verlauf in einer privaten Bibliothek festhalten.",
@@ -7614,6 +7656,7 @@
"Uninstalling Ceph...": "Ceph deinstallieren...",
"Uninstalling Fastfetch...": "Fastfetch wird deinstalliert...",
"Uninstalling Figurine...": "Figur wird deinstalliert...",
+ "Uninstalling Log2RAM...": "Log2RAM wird deinstalliert...",
"Uninstalling amdgpu_top...": "amdgpu_top wird deinstalliert...",
"Uninstalling current NVIDIA driver before installing new version": "Deinstallieren Sie den aktuellen NVIDIA-Treiber, bevor Sie eine neue Version installieren",
"Uninstalling intel-gpu-tools...": "Intel-GPU-Tools werden deinstalliert...",
@@ -8092,6 +8135,7 @@
"Warning: both VMs have autostart enabled (onboot=1).": "Warnung: Auf beiden VMs ist der Autostart aktiviert (onboot=1).",
"Warnings": "Warnungen",
"Watch history, ratings and watchlists synced between media servers and trackers.": "Wiedergabeverlauf, Bewertungen und Merklisten, synchronisiert zwischen Medienservern und Trackern.",
+ "Watcharr": "Watcharr",
"Watchdog": "Watchdog",
"Watchdog (off): restart the application when it crashes": "Watchdog (aus): Anwendung neu starten, wenn sie abstürzt",
"Watchdog (on): restart the application when it crashes": "Watchdog (ein): Anwendung neu starten, wenn sie abstürzt",
@@ -8869,10 +8913,5 @@
"⚠ Disk data will NOT be erased.": "⚠ Festplattendaten werden NICHT gelöscht.",
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Die Festplatte wird ausgehängt und aus /etc/fstab entfernt.",
"⚠ The /etc/fstab entry will be removed.": "⚠ Der /etc/fstab-Eintrag wird entfernt.",
- "⚠ The disk will be unmounted.": "⚠ Die Festplatte wird ausgehängt.",
- "Initial Umami administrator": "Erster Administrator für Umami",
- "Privacy-focused, self-hosted web analytics.": "Datenschutzorientierte, selbst gehostete Webanalyse.",
- "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Die offiziellen Compose-Dienste von Umami und PostgreSQL fordern weder den privilegierten Modus noch eine gelockerte Isolation des Hostsystems an.",
- "A self-hosted, no-code database and application platform.": "Selbst gehostete No-Code-Plattform für Datenbanken und Anwendungen.",
- "Public URL used to access Baserow (for example https://baserow.example.com)": "Öffentliche URL für den Zugriff auf Baserow (zum Beispiel https://baserow.example.com)"
+ "⚠ The disk will be unmounted.": "⚠ Die Festplatte wird ausgehängt."
}
diff --git a/lang/es.json b/lang/es.json
index 0dbee4de..10d69865 100644
--- a/lang/es.json
+++ b/lang/es.json
@@ -47,6 +47,7 @@
"A GPS tracking platform with device management and fleet reports.": "Una plataforma de seguimiento GPS con gestión de dispositivos e informes de flota.",
"A GTK Broadway web UI for libvirt and virt-manager.": "Una interfaz web GTK Broadway para libvirt y virt-manager.",
"A Personal Relationship Management tool to help you document your social life.": "Una herramienta de gestión de relaciones personales para documentar tu vida social.",
+ "A REST backend for restic backups; it has no web interface.": "Backend REST para copias de seguridad de restic, sin interfaz web.",
"A VirtIO ISO already exists. Do you want to overwrite it?": "Ya existe una ISO VirtIO. ¿Quieres sobrescribirla?",
"A ZFS pool with this name already exists.": "Ya existe un grupo ZFS con este nombre.",
"A ZFS pool with this name already exists:": "Ya existe un grupo ZFS con este nombre:",
@@ -94,6 +95,7 @@
"A lightweight dashboard that brings feeds, services and useful widgets together.": "Un panel ligero que reúne fuentes, servicios y widgets útiles.",
"A lightweight hub for monitoring servers and containers.": "Un panel ligero para supervisar servidores y contenedores.",
"A lightweight self-hosted Kanban board.": "Un tablero Kanban ligero y autoalojado.",
+ "A lightweight self-hosted URL shortener with SQLite storage.": "Acortador de URL ligero y autoalojado con almacenamiento SQLite.",
"A lightweight self-hosted music server with Subsonic API support.": "Un servidor de música ligero y autoalojado, compatible con la API de Subsonic.",
"A lightweight self-hosted pastebin for text and files.": "Un servicio ligero y autoalojado para compartir texto y archivos.",
"A lightweight static homepage for organizing self-hosted services.": "Una página de inicio estática y ligera para organizar servicios autoalojados.",
@@ -123,6 +125,7 @@
"A pending operation exists for": "Existe una operación pendiente",
"A pending stack assembly already exists; it is not overwritten": "Ya existe un ensamblado de pila pendiente; no se sobrescribe",
"A personal homepage for your apps and dashboards.": "Una página de inicio personal para tus aplicaciones y paneles.",
+ "A personal, self-hosted microblog and timeline.": "Microblog y cronología personales, autoalojados.",
"A previous NVIDIA refresh is pending review": "Un refresco previo de NVIDIA está pendiente de revisión",
"A previous VFIO passthrough configuration was detected for the following NVIDIA GPU(s):": "Se detectó una configuración de passthrough de VFIO anterior para las siguientes GPU NVIDIA:",
"A privacy-first, open-source platform for knowledge management and collaboration.": "Una plataforma de código abierto para la gestión del conocimiento y la colaboración que da prioridad a la privacidad.",
@@ -146,15 +149,18 @@
"A self-hosted Q&A and knowledge-sharing platform.": "Una plataforma autoalojada de preguntas y respuestas para compartir conocimientos.",
"A self-hosted RSS reader inspired by Google Reader.": "Lector RSS autoalojado inspirado en Google Reader.",
"A self-hosted app for organizing recurring tasks and household chores, with data stored in SQLite.": "Gestor autoalojado para organizar tareas y quehaceres recurrentes, con almacenamiento en SQLite.",
+ "A self-hosted app for organizing tabletop role-playing campaigns.": "Aplicación autoalojada para organizar campañas de juegos de rol de mesa.",
"A self-hosted bookmark manager and web archiver.": "Un gestor de marcadores y archivador web autoalojado.",
"A self-hosted bookmark manager.": "Gestor de marcadores autoalojado.",
"A self-hosted file sharing service with expiring downloads.": "Servicio autoalojado para compartir archivos con descargas que caducan.",
"A self-hosted link-in-bio page.": "Página autoalojada de enlaces para tu biografía.",
"A self-hosted read-it-later application.": "Una aplicación autoalojada para guardar artículos y leerlos más tarde.",
"A self-hosted task manager for planning work and tracking time.": "Un gestor de tareas autoalojado para planificar el trabajo y registrar el tiempo.",
+ "A self-hosted tracker for habits, tasks, notes and goals.": "Registro autoalojado de hábitos, tareas, notas y objetivos.",
"A self-hosted vehicle maintenance and fuel log.": "Un registro autoalojado del mantenimiento y consumo de combustible de tus vehículos.",
"A self-hosted workflow scheduler with a web interface.": "Planificador autoalojado de flujos de trabajo con interfaz web.",
"A self-hosted, goal-free habit tracking tool.": "Una herramienta autoalojada de seguimiento de hábitos, sin objetivos.",
+ "A self-hosted, no-code database and application platform.": "Plataforma autoalojada de bases de datos y aplicaciones sin necesidad de programar.",
"A self-improving AI agent with memory, skills, messaging, and a web dashboard.": "Un agente de IA que mejora por sí mismo, con memoria, habilidades, mensajería y un panel web.",
"A server reboot is recommended for all changes to take full effect.": "Se recomienda reiniciar el servidor para que todos los cambios surtan efecto.",
"A shared directory was replaced during the installation": "Un directorio compartido fue reemplazado durante la instalación",
@@ -274,6 +280,7 @@
"Active ZRAM algorithm, capacity or swap sources did not match the profile": "El algoritmo, la capacidad o las fuentes de swap de ZRAM activos no coincidían con el perfil",
"Active exports:": "Exportaciones activas:",
"Active session:": "Sesión activa:",
+ "Activepieces": "Activepieces",
"Actual device path on the host": "Ruta real del dispositivo en el host",
"Adaptation file too large": "Archivo de adaptación demasiado grande",
"Adaptation file with unexpected permissions or owner": "Archivo de adaptación con permisos inesperados o propietario",
@@ -781,6 +788,8 @@
"Build and install the gasket and apex kernel modules (DKMS)": "Compilar e instalar los módulos del kernel gasket y apex (DKMS)",
"Build dependencies installed.": "Construya dependencias instaladas.",
"Build dynamic websites using SQL files.": "Crea sitios web dinámicos a partir de archivos SQL.",
+ "Build reports and dashboards from your data.": "Crea informes y paneles a partir de tus datos.",
+ "Build visual automations for personal and hobby use.": "Crea automatizaciones visuales para uso personal o aficiones.",
"Build your personal knowledge base with TriliumNext Notes": "Crea tu base de conocimiento personal con TriliumNext Notes",
"Bulk generation requires GITHUB_TOKEN to obtain an immutable revision per application": "La generación masiva requiere GITHUB_TOKEN para obtener una revisión inmutable por aplicación",
"Business & ERP": "Business & ERP",
@@ -826,6 +835,7 @@
"Calibre-web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. It is also possible to integrate google drive and edit metadata and your calibre library through the app itself.": "Calibre-web es una aplicación web con una interfaz limpia para explorar, leer y descargar eBooks a partir de una base de datos de Calibre existente. También permite integrar Google Drive y editar los metadatos y tu biblioteca de Calibre desde la propia aplicación.",
"Calligra is an office and graphic art suite by KDE. It is available for desktop PCs, tablet computers, and smartphones. It contains applications for word processing, spreadsheets, presentation, vector graphics, and editing databases.": "Calligra es una suite ofimática y de artes gráficas de KDE. Está disponible para PC de escritorio, tabletas y smartphones. Incluye aplicaciones de procesador de textos, hojas de cálculo, presentaciones, gráficos vectoriales y edición de bases de datos.",
"Calnode": "Calnode",
+ "Campfire first-run setup": "Configuración inicial de Campfire",
"Cancel": "Cancelar",
"Cancel restore": "Cancelar restauración",
"Cancel this setup": "Cancelar esta configuración",
@@ -973,6 +983,8 @@
"Checklist post-upgrade finished. Warnings:": "Lista de verificación posterior a la actualización finalizada. Advertencias:",
"Checklist pre-check finished. Warnings:": "Verificación previa de la lista de verificación finalizada. Advertencias:",
"Checks for LVM and storage issues": "Comprueba si hay problemas de almacenamiento y LVM",
+ "Chhoto URL": "Chhoto URL",
+ "Chhoto URL administrator password": "Contraseña de administrador de Chhoto URL",
"Choose": "Elige",
"Choose BIOS Type": "Elige el tipo de BIOS",
"Choose BIOS type": "Elige el tipo de BIOS",
@@ -1192,6 +1204,7 @@
"Configured during installation": "Se configura durante la instalación.",
"Configured import disk %s as %s": "Disco importado %s configurado como %s",
"Configured loader disk as ide0": "Disco del cargador configurado como ide0",
+ "Configured username": "Nombre de usuario configurado",
"Configured virtual disk as %s, %sGB on %s": "Disco virtual configurado como %s, %sGB en %s",
"Configuring AMD GPU passthrough...": "Configurando el passthrough de GPU AMD...",
"Configuring AMD softdep...": "Configurando el software AMD...",
@@ -1599,6 +1612,7 @@
"Create and stream custom live TV channels from your media library.": "Crea y emite canales de televisión en directo a partir de tu biblioteca multimedia.",
"Create credentials file (recommended):": "Crear archivo de credenciales (recomendado):",
"Create directory": "Crear directorio",
+ "Create encrypted, deduplicated backups and restore files.": "Crea copias de seguridad cifradas y deduplicadas y restaura archivos.",
"Create export directory:": "Crear directorio de exportación:",
"Create mount point:": "Crear punto de montaje:",
"Create new directory in /mnt": "Crear nuevo directorio en /mnt",
@@ -1609,9 +1623,11 @@
"Create share directory:": "Crear directorio compartido:",
"Create shared directory:": "Crear directorio compartido:",
"Create this LXC in privileged mode?": "¿Crear este LXC en modo privilegiado?",
+ "Create with create_user in the container console": "Crea el usuario con create_user desde la consola del contenedor",
"Create, send and sign documents with a self-hosted web app.": "Crea, envía y firma documentos con una aplicación web autoalojada.",
"Created common remapped user": "Usuario común reasignado creado",
"Created directory on host:": "Directorio creado en el host:",
+ "Created during first-run setup": "Se crea durante la configuración inicial",
"Created persistent names for": "Nombres persistentes creados para",
"Created remapped user": "Usuario reasignado creado",
"Created sharedfiles group (GID: 101000)": "Grupo de archivos compartidos creado (GID: 101000)",
@@ -2125,6 +2141,7 @@
"Each LUN will appear as a block device assignable to VMs.": "Cada LUN aparecerá como un dispositivo de bloque asignable a las VM.",
"Each peer gets its configuration and its QR code inside the container: /config/peer1/peer1.conf and /config/peer1/peer1.png, or /config/peer_/peer_.conf when names were given.": "Cada peer recibe su configuración y su código QR dentro del contenedor: /config/peer1/peer1.conf y /config/peer1/peer1.png, o /config/peer_/peer_.conf si se dieron nombres.",
"Ebook and audiobook collection manager for Usenet and BitTorrent users.": "Gestor de colecciones de ebooks y audiolibros para usuarios de Usenet y BitTorrent.",
+ "Ech0": "Ech0",
"Eden is an experimental open-source emulator for the Nintendo Switch, built with performance and stability in mind.": "Eden es un emulador experimental y de código abierto de Nintendo Switch, creado pensando en el rendimiento y la estabilidad.",
"Edge TPU runtime installed.": "Tiempo de ejecución de Edge TPU instalado.",
"Edit": "Editar",
@@ -2134,6 +2151,7 @@
"Edit the VM machine type to q35 and try again.": "Edita el tipo de máquina VM a q35 y vuelve a intentarlo.",
"Edition:": "Edición:",
"Email address for certificate expiry notices (required by ZeroSSL)": "Dirección de correo para los avisos de caducidad del certificado (obligatoria con ZeroSSL)",
+ "Email address for the OpenHabitTracker account": "Correo electrónico de la cuenta de OpenHabitTracker",
"Email address of the LibreDB Studio administrator": "Dirección de correo del administrador de LibreDB Studio",
"Email address of the NetBox admin account": "Dirección de correo de la cuenta de administrador de NetBox",
"Email address of the admin account": "Correo electrónico de la cuenta admin",
@@ -2297,6 +2315,7 @@
"Enter the BORG REPOKEY passphrase for target:": "Introduce la frase de contraseña de BORG REPOKEY para el destino:",
"Enter the Samba username:": "Introduce el nombre de usuario de Samba:",
"Enter the VMID for the new VM:": "Introduce el VMID para la nueva VM:",
+ "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Introduce la URL base que usarán los clientes y proveedores de webhooks para acceder a esta instancia de Activepieces.",
"Enter the container terminal:": "Entrar en la terminal del contenedor:",
"Enter the full path to the directory containing disk images:": "Introduce la ruta completa al directorio que contiene las imágenes del disco:",
"Enter the full path to the host directory:": "Introduce la ruta completa al directorio del host:",
@@ -2665,7 +2684,9 @@
"Firewall allows port": "El cortafuegos permite el puerto",
"Firewall settings": "Configuración del cortafuegos",
"Firmware :": "Firmware:",
+ "First Ech0 account (becomes Owner)": "Primera cuenta de Ech0 (será la propietaria)",
"First Jelu administrator": "Primer administrador de Jelu",
+ "First Watcharr account (becomes administrator)": "Primera cuenta de Watcharr (será la administradora)",
"First complete DSM setup and verify Web UI/SSH access.": "Primero completa la configuración de DSM y comprueba el acceso a la interfaz de usuario web/SSH.",
"First complete ZimaOS setup and verify remote access (web/SSH).": "Primero completa la configuración de ZimaOS y comprueba el acceso remoto (web/SSH).",
"First install the GPU drivers inside the guest and verify remote access (RDP/SSH).": "Primero instala los controladores de GPU dentro de la VM y comprueba el acceso remoto (RDP/SSH).",
@@ -3303,6 +3324,7 @@
"Initial Redmine administrator": "Administrador inicial de Redmine",
"Initial Stirling-PDF account": "Cuenta inicial de Stirling-PDF",
"Initial Tandoor administrator": "Administrador inicial de Tandoor",
+ "Initial Umami administrator": "Administrador inicial de Umami",
"Initial administrator created:": "Administrador inicial creado:",
"Initial administrator user": "Usuario administrador inicial",
"Initializing Borg repository if needed...": "Inicializando el repositorio de Borg si es necesario...",
@@ -3401,6 +3423,7 @@
"Installing Fail2Ban...": "Instalando Fail2Ban...",
"Installing Git as a prerequisite...": "Instalar Git como requisito previo...",
"Installing Intel VA-API drivers in container...": "Instalación de controladores Intel VA-API en contenedor...",
+ "Installing Log2RAM": "Instalando Log2RAM",
"Installing Log2RAM from GitHub...": "Instalando Log2RAM desde GitHub...",
"Installing Log2RAM from source...": "Instalando Log2RAM desde la fuente...",
"Installing NFS Client in LXC": "Instalación del cliente NFS en LXC",
@@ -3750,6 +3773,8 @@
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa es una potente herramienta que te da control total sobre tus bibliotecas multimedia. Con Kometa llevas la personalización al siguiente nivel, con control detallado de metadatos, colecciones, overlays y mucho más.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa lee su configuración de /config/config.yml y el contenedor solo incluye /config/config.yml.template. Copia la plantilla a config.yml, rellena las conexiones obligatorias de Plex y TMDb y reinicia el contenedor.",
"Komga is a media server for your comics, mangas, BDs, magazines and eBooks.": "Komga es un servidor multimedia para tus cómics, mangas, BDs, revistas y eBooks.",
+ "Kopia Web UI": "Interfaz web de Kopia",
+ "Kopia Web UI login": "Acceso a la interfaz web de Kopia",
"Krita is a professional FREE and open source painting program. It is made by artists that want to see affordable art tools for everyone.": "Krita es un programa de pintura profesional, GRATUITO y de código abierto. Lo hacen artistas que quieren herramientas de arte asequibles para todos.",
"LAN access to the kept containers (stack configuration incomplete):": "Acceso LAN a los contenedores conservados (configuración de la pila incompleta):",
"LAN address applied to the application URLs": "Dirección LAN aplicada a las URL de la aplicación",
@@ -3890,10 +3915,14 @@
"Log aggregation server for Grafana.": "Servidor de agregación de registros para Grafana.",
"Log file": "Archivo de log",
"Log2RAM already registered — updating to latest configuration": "Log2RAM ya registrado: actualizando a la última configuración",
+ "Log2RAM could not be downloaded": "No se ha podido descargar Log2RAM",
"Log2RAM installation and configuration completed successfully.": "La instalación y configuración de Log2RAM se completó con éxito.",
"Log2RAM installation cancelled by user": "Instalación de Log2RAM cancelada por el usuario",
"Log2RAM installation verification failed. Check /tmp/log2ram_install.log": "Error en la verificación de la instalación de Log2RAM. Comprueba /tmp/log2ram_install.log",
"Log2RAM installed successfully": "Log2RAM se instaló correctamente",
+ "Log2RAM installed:": "Log2RAM instalado:",
+ "Log2RAM is removed at the next reboot, after writing /var/log to disk": "Log2RAM se elimina en el próximo reinicio, después de escribir /var/log en disco",
+ "Log2RAM keeps /var/log in RAM; the journal stays on disk from the next boot": "Log2RAM mantiene /var/log en RAM; el journal se queda en disco a partir del próximo arranque",
"Log2RAM keeps the journal in order from the next reboot": "Log2RAM mantiene el diario del sistema en orden a partir del próximo reinicio",
"Log2RAM size set to:": "Tamaño de Log2RAM establecido en:",
"Log2RAM write scheduled every": "Escritura de Log2RAM programada cada",
@@ -3976,6 +4005,7 @@
"Manage custom backup paths": "Configurar rutas de backup personalizadas",
"Manage custom paths (add / remove your folders)": "Configurar rutas personalizadas (añadir/eliminar carpetas)",
"Manage data and APIs with a modern visual interface.": "Gestiona datos y APIs con una interfaz visual moderna.",
+ "Manage database changes, access and workflows.": "Gestiona cambios en bases de datos, accesos y flujos de trabajo.",
"Manage installed OCI applications": "Gestionar aplicaciones OCI instaladas",
"Manage local backup target": "Configurar el destino del backup local",
"Manage private image registries.\nSaved credentials let ProxMenux access your private images.": "Gestiona los registros de imágenes privadas.\nCon las credenciales guardadas, ProxMenux puede acceder a tus imágenes privadas.",
@@ -4839,6 +4869,9 @@
"Open-source in-memory data store.": "Almacén de datos en memoria de código abierto.",
"Open-source self-hosted SQL IDE.": "IDE de SQL autoalojado y de código abierto.",
"OpenClaw is a personal AI assistant you run on your own devices": "OpenClaw es un asistente personal de IA que ejecutas en tus propios dispositivos",
+ "OpenHabitTracker": "OpenHabitTracker",
+ "OpenHabitTracker account": "Cuenta de OpenHabitTracker",
+ "OpenHabitTracker initial username": "Nombre de usuario inicial de OpenHabitTracker",
"OpenList": "OpenList",
"OpenObserve administrator": "Administrador de OpenObserve",
"OpenShot Video Editor is an award-winning free and open-source video editor for Linux, Mac, and Windows, and is dedicated to delivering high quality video editing and animation solutions to the world.": "OpenShot Video Editor es un galardonado editor de vídeo gratuito y de código abierto para Linux, Mac y Windows, y está dedicado a ofrecer soluciones de edición y animación de vídeo de alta calidad al mundo.",
@@ -5206,6 +5239,7 @@
"Printer": "Impresora",
"Privacy-first finance app with envelope budgeting and multi-device sync.": "Aplicación de finanzas que da prioridad a la privacidad, con presupuestos por sobres y sincronización entre dispositivos.",
"Privacy-first, self-hosted PDF toolkit": "Kit de herramientas PDF autoalojado que da prioridad a la privacidad",
+ "Privacy-focused, self-hosted web analytics.": "Analítica web autoalojada y centrada en la privacidad.",
"Private installation record saved": "Registro de instalación privado guardado",
"Private network assigned automatically:": "Red privada asignada automáticamente:",
"Private network kept, because other guests still use it:": "Red privada conservada, porque otras VM o CT la siguen usando:",
@@ -5328,6 +5362,7 @@
"Public UDP port clients connect to": "Puerto UDP público al que se conectan los clientes",
"Public UDP port peers connect to": "Puerto UDP público al que se conectan los peers",
"Public URL of phpMyAdmin when it is served behind a reverse proxy": "URL pública de phpMyAdmin cuando se sirve detrás de un proxy inverso",
+ "Public URL used to access Baserow (for example https://baserow.example.com)": "URL pública para acceder a Baserow (por ejemplo, https://baserow.example.com)",
"Public address clients connect to (vpn.example.com or a public IP)": "Dirección pública a la que se conectan los clientes (vpn.example.com o una IP pública)",
"Public address peers connect to, or auto to detect it (vpn.example.com)": "Dirección pública a la que se conectan los peers, o auto para detectarla (vpn.example.com)",
"Publish each directory from Resources: open it, switch on SMB, NFS or both, and save.": "Publica cada directorio desde Recursos: ábrelo, activa SMB, NFS o ambos y guarda.",
@@ -5353,6 +5388,7 @@
"RAM in MiB": "RAM en MiB",
"REPAIR SUMMARY": "RESUMEN DE REPARACIÓN",
"REQUIREMENTS:": "REQUISITOS:",
+ "REST API Basic Authentication user": "Usuario de autenticación básica para la API REST",
"ROCm does not support this GPU officially. The application can use it by presenting it as another generation of its family: it is faster than the CPU, but under load the GPU can stop responding, and then the application does not answer until its container is restarted. The ROCm image is also larger than the others.": "ROCm no soporta oficialmente esta GPU. La aplicación puede usarla presentándola como otra generación de su familia: es más rápida que la CPU, pero bajo carga la GPU puede dejar de responder, y entonces la aplicación no contesta hasta que se reinicia su contenedor. Además, la imagen de ROCm es más grande que las demás.",
"ROCm requires /dev/kfd on the host": "ROCm requiere /dev/kfd en el host",
"ROCm requires the render device of an AMD GPU": "ROCm requiere el dispositivo de render de una GPU AMD",
@@ -5656,6 +5692,7 @@
"Repositories switched to no-subscription": "Los repositorios cambiaron a sin suscripción",
"Repository check failed. NVIDIA installation stopped.": "La comprobación de los repositorios ha fallado. La instalación de NVIDIA se ha detenido.",
"Repository check failed. Update stopped.": "La comprobación de los repositorios ha fallado. La actualización se ha detenido.",
+ "Repository encryption password": "Contraseña de cifrado del repositorio",
"Repository helper unavailable. NVIDIA installation stopped.": "El asistente de repositorios no está disponible. La instalación de NVIDIA se ha detenido.",
"Repository policy returned an unexpected result.": "La política de repositorios ha devuelto un resultado inesperado.",
"Repository ready.": "Repositorio listo.",
@@ -5773,6 +5810,7 @@
"Root shell/profile config": "Configuración de perfil/shell raíz",
"Root user on the PVE host (default 'root'):": "Usuario root en el host PVE ('root' predeterminado):",
"Rootfs size in GB": "Tamaño del rootfs en GB",
+ "Rotate and write /var/log to disk when it passes 85% of its size?": "¿Rotar y escribir /var/log en disco cuando supere el 85% de su tamaño?",
"Rotate the recovery passphrase": "Rotar la frase de contraseña de recuperación",
"Routing Information": "Información de enrutamiento",
"Routing Table": "Tabla de enrutamiento",
@@ -6245,6 +6283,7 @@
"Set ownership and permissions:": "Establecer propiedad y permisos:",
"Set the initial Technitium DNS administrator password": "Establece la contraseña inicial del administrador de Technitium DNS.",
"Set this disk as the primary boot disk?": "¿Configurar este disco como disco de arranque principal?",
+ "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Establécelo en 1 solo en una LAN privada que utilice exclusivamente HTTP; en los demás casos, usa HTTPS.",
"Set up custom MOTD banner": "Configurar banner MOTD personalizado",
"Set up the apex group and udev rules": "Configurar el grupo apex y las reglas de udev",
"Set up time synchronization and entropy generation": "Configurar la sincronización horaria y la generación de entropía",
@@ -6948,6 +6987,7 @@
"The domain must resolve to the public address of this network before the certificate can be issued.": "El dominio debe resolver a la dirección pública de esta red antes de poder emitir el certificado.",
"The download client still needs to be configured.": "El cliente de descarga todavía necesita ser configurado.",
"The download stopped progressing; cancelling this attempt.": "La descarga dejó de progresar; cancelando este intento.",
+ "The downloaded Log2RAM does not match the expected release": "El Log2RAM descargado no coincide con la versión esperada",
"The downloaded image does not match its manifest": "La imagen descargada no coincide con su manifiesto",
"The downloaded image is corrupt:": "La imagen descargada es corrupta:",
"The dpkg package database is clean.": "La base de datos de paquetes de dpkg está limpia.",
@@ -7094,6 +7134,7 @@
"The oci-compose is incomplete: it does not say which image to install.": "El oci-compose está incompleto: no dice qué imagen instalar.",
"The oci-compose is not valid JSON:": "El oci-compose no es JSON válido:",
"The official Tandoor startup executable is missing": "Falta el ejecutable oficial de arranque de Tandoor",
+ "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Los servicios oficiales de Umami y PostgreSQL en Compose no solicitan el modo privilegiado ni una relajación del aislamiento del host.",
"The official inventory contains no NVIDIA devices": "El inventario oficial no contiene dispositivos NVIDIA",
"The official inventory contains no NVIDIA driver components": "El inventario oficial no contiene componentes de controlador NVIDIA",
"The official startup cannot be reproduced": "La startup oficial no puede reproducirse",
@@ -7510,6 +7551,7 @@
"Total routes": "Rutas totales",
"Total size:": "Tamaño total:",
"Toxiproxy": "Toxiproxy",
+ "Track movies, television, anime and games you want to enjoy.": "Lleva un registro de películas, series, anime y videojuegos que quieras disfrutar.",
"Track subscriptions and recurring expenses.": "Seguimiento de suscripciones y gastos recurrentes.",
"Track your music listening history on your own server.": "Registra tu historial de reproducción musical en tu propio servidor.",
"Track your reading, books and history in a private library.": "Registra tus lecturas, libros e historial en una biblioteca privada.",
@@ -7589,6 +7631,7 @@
"Uninstalling Ceph...": "Desinstalando Ceph...",
"Uninstalling Fastfetch...": "Desinstalando Fastfetch...",
"Uninstalling Figurine...": "Desinstalando Figurine...",
+ "Uninstalling Log2RAM...": "Desinstalando Log2RAM...",
"Uninstalling amdgpu_top...": "Desinstalando amdgpu_top...",
"Uninstalling current NVIDIA driver before installing new version": "Desinstalar el controlador NVIDIA actual antes de instalar una nueva versión",
"Uninstalling intel-gpu-tools...": "Desinstalando intel-gpu-tools...",
@@ -8067,6 +8110,7 @@
"Warning: both VMs have autostart enabled (onboot=1).": "Advertencia: ambas máquinas virtuales tienen el inicio automático habilitado (onboot=1).",
"Warnings": "Advertencias",
"Watch history, ratings and watchlists synced between media servers and trackers.": "Historial, valoraciones y listas de seguimiento sincronizados entre servidores multimedia y trackers.",
+ "Watcharr": "Watcharr",
"Watchdog": "Vigilancia",
"Watchdog (off): restart the application when it crashes": "Vigilancia (desactivada): reiniciar la aplicación cuando falla",
"Watchdog (on): restart the application when it crashes": "Vigilancia (activada): reiniciar la aplicación cuando falla",
@@ -8841,10 +8885,5 @@
"⚠ Disk data will NOT be erased.": "⚠ Los datos del disco NO se borrarán.",
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ El disco se desmontará y se eliminará de /etc/fstab.",
"⚠ The /etc/fstab entry will be removed.": "⚠ Se eliminará la entrada /etc/fstab.",
- "⚠ The disk will be unmounted.": "⚠ El disco se desmontará.",
- "Initial Umami administrator": "Administrador inicial de Umami",
- "Privacy-focused, self-hosted web analytics.": "Analítica web autoalojada y centrada en la privacidad.",
- "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Los servicios oficiales de Umami y PostgreSQL en Compose no solicitan el modo privilegiado ni una relajación del aislamiento del host.",
- "A self-hosted, no-code database and application platform.": "Plataforma autoalojada de bases de datos y aplicaciones sin necesidad de programar.",
- "Public URL used to access Baserow (for example https://baserow.example.com)": "URL pública para acceder a Baserow (por ejemplo, https://baserow.example.com)"
+ "⚠ The disk will be unmounted.": "⚠ El disco se desmontará."
}
diff --git a/lang/fr.json b/lang/fr.json
index db34dd93..ad7891f6 100644
--- a/lang/fr.json
+++ b/lang/fr.json
@@ -47,6 +47,7 @@
"A GPS tracking platform with device management and fleet reports.": "Une plateforme de suivi GPS avec gestion des appareils et rapports de flotte.",
"A GTK Broadway web UI for libvirt and virt-manager.": "Une interface web GTK Broadway pour libvirt et virt-manager.",
"A Personal Relationship Management tool to help you document your social life.": "Une relation personnelle Outil de gestion pour vous aider à documenter votre vie sociale.",
+ "A REST backend for restic backups; it has no web interface.": "Un backend REST pour les sauvegardes restic, sans interface web.",
"A VirtIO ISO already exists. Do you want to overwrite it?": "Un ISO VirtIO existe déjà. Voulez-vous l'écraser ?",
"A ZFS pool with this name already exists.": "Un pool ZFS portant ce nom existe déjà.",
"A ZFS pool with this name already exists:": "Un pool ZFS portant ce nom existe déjà :",
@@ -94,6 +95,7 @@
"A lightweight dashboard that brings feeds, services and useful widgets together.": "Un tableau de bord léger qui réunit flux, services et widgets utiles.",
"A lightweight hub for monitoring servers and containers.": "Un outil léger pour surveiller les serveurs et les conteneurs.",
"A lightweight self-hosted Kanban board.": "Un tableau Kanban léger et auto-hébergé.",
+ "A lightweight self-hosted URL shortener with SQLite storage.": "Un raccourcisseur d’URL léger et auto-hébergé avec stockage SQLite.",
"A lightweight self-hosted music server with Subsonic API support.": "Un serveur de musique léger et auto-hébergé, compatible avec l’API Subsonic.",
"A lightweight self-hosted pastebin for text and files.": "Un service léger et auto-hébergé de partage de texte et de fichiers.",
"A lightweight static homepage for organizing self-hosted services.": "Une page d’accueil statique et légère pour organiser les services auto-hébergés.",
@@ -124,6 +126,7 @@
"A pending operation exists for": "Un opera en attente existe pour",
"A pending stack assembly already exists; it is not overwritten": "Une pile en attente existe déjà; elle n'est pas écrasée",
"A personal homepage for your apps and dashboards.": "Une page d’accueil personnelle pour vos applications et tableaux de bord.",
+ "A personal, self-hosted microblog and timeline.": "Un microblog et un fil d’actualité personnels, auto-hébergés.",
"A previous NVIDIA refresh is pending review": "Une mise à jour antérieure de la NVIDIA est en attente d'examen",
"A previous VFIO passthrough configuration was detected for the following NVIDIA GPU(s):": "Une configuration de relais VFIO précédente a été détectée pour le(s) GPU NVIDIA suivant :",
"A privacy-first, open-source platform for knowledge management and collaboration.": "Une plate-forme de gestion et de collaboration des connaissances ouverte et axée sur la protection de la vie privée.",
@@ -147,15 +150,18 @@
"A self-hosted Q&A and knowledge-sharing platform.": "Une plateforme auto-hébergée de questions-réponses et de partage des connaissances.",
"A self-hosted RSS reader inspired by Google Reader.": "Lecteur RSS auto-hébergé inspiré de Google Reader.",
"A self-hosted app for organizing recurring tasks and household chores, with data stored in SQLite.": "Application auto-hébergée pour organiser les tâches et corvées récurrentes, avec stockage SQLite.",
+ "A self-hosted app for organizing tabletop role-playing campaigns.": "Une application auto-hébergée pour organiser des campagnes de jeux de rôle sur table.",
"A self-hosted bookmark manager and web archiver.": "Un gestionnaire de favoris et un outil d’archivage web auto-hébergés.",
"A self-hosted bookmark manager.": "Gestionnaire de favoris auto-hébergé.",
"A self-hosted file sharing service with expiring downloads.": "Service de partage de fichiers auto-hébergé avec des liens de téléchargement temporaires.",
"A self-hosted link-in-bio page.": "Page de liens en bio auto-hébergée.",
"A self-hosted read-it-later application.": "Une application auto-hébergée pour sauvegarder des articles à lire plus tard.",
"A self-hosted task manager for planning work and tracking time.": "Un gestionnaire de tâches auto-hébergé pour planifier le travail et suivre le temps passé.",
+ "A self-hosted tracker for habits, tasks, notes and goals.": "Un outil auto-hébergé pour suivre les habitudes, tâches, notes et objectifs.",
"A self-hosted vehicle maintenance and fuel log.": "Un carnet auto-hébergé pour suivre l’entretien et la consommation de vos véhicules.",
"A self-hosted workflow scheduler with a web interface.": "Planificateur de workflows auto-hébergé avec interface web.",
"A self-hosted, goal-free habit tracking tool.": "Un outil de suivi de l'habitude autonome et sans but.",
+ "A self-hosted, no-code database and application platform.": "Plateforme auto-hébergée de bases de données et d’applications sans code.",
"A self-improving AI agent with memory, skills, messaging, and a web dashboard.": "Un agent d'IA auto-améliorant avec mémoire, compétences, messagerie, et un tableau de bord web.",
"A server reboot is recommended for all changes to take full effect.": "Un redémarrage du serveur est recommandé pour que toutes les modifications prennent pleinement effet.",
"A shared directory was replaced during the installation": "Un répertoire partagé a été remplacé pendant l'installation",
@@ -276,6 +282,7 @@
"Active ZRAM algorithm, capacity or swap sources did not match the profile": "L'algorithme, la capacité ou les sources d'échange ZRAM actifs ne correspondaient pas au profil",
"Active exports:": "Exportations actives :",
"Active session:": "Séance active :",
+ "Activepieces": "Activepieces",
"Actual device path on the host": "Chemin réel du périphérique sur l'hôte",
"Adaptation file too large": "Dossier d'adaptation trop grand",
"Adaptation file with unexpected permissions or owner": "Fichier d'adaptation avec permissions inattendues ou propriétaire",
@@ -783,6 +790,8 @@
"Build and install the gasket and apex kernel modules (DKMS)": "Compiler et installer les modules noyau gasket et apex (DKMS)",
"Build dependencies installed.": "Dépendances de build installées.",
"Build dynamic websites using SQL files.": "Créer des sites web dynamiques à partir de fichiers SQL.",
+ "Build reports and dashboards from your data.": "Créez des rapports et des tableaux de bord à partir de vos données.",
+ "Build visual automations for personal and hobby use.": "Créez des automatisations visuelles pour un usage personnel ou vos loisirs.",
"Build your personal knowledge base with TriliumNext Notes": "Construisez votre base de connaissances personnelles avec TriliumNext Notes",
"Bulk generation requires GITHUB_TOKEN to obtain an immutable revision per application": "La génération en masse nécessite GITHUB_TOKEN pour obtenir une révision immuable par application",
"Business & ERP": "Entreprise et ERP",
@@ -827,6 +836,7 @@
"Calibre-web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. It is also possible to integrate google drive and edit metadata and your calibre library through the app itself.": "Calibre-web est une application web offrant une interface propre pour la navigation, la lecture et le téléchargement de livres électroniques à l'aide d'une base de données existante Calibre. Il est également possible d'intégrer google drive et éditer des métadonnées et votre bibliothèque calibre via l'application elle-même.",
"Calligra is an office and graphic art suite by KDE. It is available for desktop PCs, tablet computers, and smartphones. It contains applications for word processing, spreadsheets, presentation, vector graphics, and editing databases.": "Calligra est une suite de bureau et d'art graphique de KDE. Il est disponible pour les ordinateurs de bureau, les tablettes et les smartphones. Il contient des applications pour le traitement de texte, les feuilles de calcul, la présentation, les graphiques vectoriels et l'édition des bases de données.",
"Calnode": "Calnode",
+ "Campfire first-run setup": "Configuration initiale de Campfire",
"Cancel": "Annuler",
"Cancel restore": "Annuler la restauration",
"Cancel this setup": "Annuler cette configuration",
@@ -974,6 +984,8 @@
"Checklist post-upgrade finished. Warnings:": "Liste de contrôle après la mise à niveau terminée. Avertissements :",
"Checklist pre-check finished. Warnings:": "Pré-vérification de la liste de contrôle terminée. Avertissements :",
"Checks for LVM and storage issues": "Vérifie les problèmes de LVM et de stockage",
+ "Chhoto URL": "Chhoto URL",
+ "Chhoto URL administrator password": "Mot de passe administrateur de Chhoto URL",
"Choose": "Choisissez",
"Choose BIOS Type": "Choisissez le type de BIOS",
"Choose BIOS type": "Choisissez le type de BIOS",
@@ -1192,6 +1204,7 @@
"Configured during installation": "Configuré lors de l’installation",
"Configured import disk %s as %s": "Disque d'importation %s configuré en tant que %s",
"Configured loader disk as ide0": "Disque du chargeur configuré en tant que ide0",
+ "Configured username": "Nom d’utilisateur configuré",
"Configured virtual disk as %s, %sGB on %s": "Disque virtuel configuré en tant que %s, %sGB sur %s",
"Configuring AMD GPU passthrough...": "Configuration du relais GPU AMD...",
"Configuring AMD softdep...": "Configuration du logiciel AMD...",
@@ -1601,6 +1614,7 @@
"Create and stream custom live TV channels from your media library.": "Créez et diffusez des chaînes de télévision en direct à partir de votre médiathèque.",
"Create credentials file (recommended):": "Créer un fichier d'informations d'identification (recommandé) :",
"Create directory": "Créer un répertoire",
+ "Create encrypted, deduplicated backups and restore files.": "Créez des sauvegardes chiffrées et dédupliquées, puis restaurez vos fichiers.",
"Create export directory:": "Créer un répertoire d'exportation :",
"Create mount point:": "Créer un point de montage :",
"Create new directory in /mnt": "Créer un nouveau répertoire dans /mnt",
@@ -1611,9 +1625,11 @@
"Create share directory:": "Créer un répertoire de partage :",
"Create shared directory:": "Créer un répertoire partagé :",
"Create this LXC in privileged mode?": "Créer ce LXC en mode privilégié ?",
+ "Create with create_user in the container console": "Créez l’utilisateur avec create_user dans la console du conteneur",
"Create, send and sign documents with a self-hosted web app.": "Créez, envoyez et signez des documents avec une application web auto-hébergée.",
"Created common remapped user": "Création d'un utilisateur remappé commun",
"Created directory on host:": "Répertoire créé sur l'hôte :",
+ "Created during first-run setup": "Créé lors de la configuration initiale",
"Created persistent names for": "Création de noms persistants pour",
"Created remapped user": "Utilisateur remappé créé",
"Created sharedfiles group (GID: 101000)": "Groupe de fichiers partagés créé (GID : 101000)",
@@ -2127,6 +2143,7 @@
"Each LUN will appear as a block device assignable to VMs.": "Chaque LUN apparaîtra comme un périphérique bloc attribuable aux machines virtuelles.",
"Each peer gets its configuration and its QR code inside the container: /config/peer1/peer1.conf and /config/peer1/peer1.png, or /config/peer_/peer_.conf when names were given.": "Chaque pair obtient sa configuration et son code QR à l'intérieur du conteneur : /config/peer1/peer1.conf et /config/peer1/peer1.png, ou /config/peer /peer .conf lorsque les noms ont été donnés.",
"Ebook and audiobook collection manager for Usenet and BitTorrent users.": "Gestionnaire de collection de livres électroniques et audio pour utilisateurs Usenet et BitTorrent.",
+ "Ech0": "Ech0",
"Eden is an experimental open-source emulator for the Nintendo Switch, built with performance and stability in mind.": "Eden est un émulateur open source expérimental pour le commutateur Nintendo, construit avec des performances et une stabilité à l'esprit.",
"Edge TPU runtime installed.": "Le runtime Edge TPU est installé.",
"Edit": "Modifier",
@@ -2136,6 +2153,7 @@
"Edit the VM machine type to q35 and try again.": "Modifiez le type de machine virtuelle en q35 et réessayez.",
"Edition:": "Édition :",
"Email address for certificate expiry notices (required by ZeroSSL)": "Adresse électronique des avis d'expiration du certificat (required by ZeroSSL)",
+ "Email address for the OpenHabitTracker account": "Adresse e-mail du compte OpenHabitTracker",
"Email address of the LibreDB Studio administrator": "Adresse électronique de l'administrateur LibreDB Studio",
"Email address of the NetBox admin account": "Adresse électronique du compte administrateur NetBox",
"Email address of the admin account": "Adresse e-mail du compte administrateur",
@@ -2299,6 +2317,7 @@
"Enter the BORG REPOKEY passphrase for target:": "saisissez la phrase secrète BORG REPOKEY pour la cible :",
"Enter the Samba username:": "Entrez le nom d'utilisateur Samba :",
"Enter the VMID for the new VM:": "Saisissez le VMID de la nouvelle VM :",
+ "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Saisissez l’URL de base que les clients et fournisseurs de webhooks utiliseront pour accéder à cette instance d’Activepieces.",
"Enter the container terminal:": "Entrez dans le terminal à conteneurs :",
"Enter the full path to the directory containing disk images:": "Entrez le chemin complet du répertoire contenant les images disque :",
"Enter the full path to the host directory:": "Entrez le chemin complet du répertoire hôte :",
@@ -2668,7 +2687,9 @@
"Firewall allows port": "Le pare-feu autorise le port",
"Firewall settings": "Paramètres du pare-feu",
"Firmware :": "Micrologiciel :",
+ "First Ech0 account (becomes Owner)": "Premier compte Ech0 (il devient propriétaire)",
"First Jelu administrator": "Premier administrateur Jelu",
+ "First Watcharr account (becomes administrator)": "Premier compte Watcharr (il devient administrateur)",
"First complete DSM setup and verify Web UI/SSH access.": "Terminez d’abord la configuration de DSM et vérifiez l’accès à l’interface utilisateur Web/SSH.",
"First complete ZimaOS setup and verify remote access (web/SSH).": "Terminez d’abord la configuration de ZimaOS et vérifiez l’accès à distance (web/SSH).",
"First install the GPU drivers inside the guest and verify remote access (RDP/SSH).": "Installez d'abord les pilotes GPU à l'intérieur de l'invité et vérifiez l'accès à distance (RDP/SSH).",
@@ -3306,6 +3327,7 @@
"Initial Redmine administrator": "Premier administrateur Redmine",
"Initial Stirling-PDF account": "Compte initial Stirling-PDF",
"Initial Tandoor administrator": "Administrateur Tandoor initial",
+ "Initial Umami administrator": "Administrateur initial d’Umami",
"Initial administrator created:": "Administrateur initial créé :",
"Initial administrator user": "Administrateur initial",
"Initializing Borg repository if needed...": "Initialisation du référentiel Borg si nécessaire...",
@@ -3404,6 +3426,7 @@
"Installing Fail2Ban...": "Installation de Fail2Ban...",
"Installing Git as a prerequisite...": "Installer Git comme prérequis...",
"Installing Intel VA-API drivers in container...": "Installation des pilotes Intel VA-API dans le conteneur...",
+ "Installing Log2RAM": "Installation de Log2RAM",
"Installing Log2RAM from GitHub...": "Installation de Log2RAM depuis GitHub...",
"Installing Log2RAM from source...": "Installation de Log2RAM à partir des sources...",
"Installing NFS Client in LXC": "Installation du client NFS dans LXC",
@@ -3753,6 +3776,8 @@
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa est un outil puissant conçu pour vous donner un contrôle complet sur vos bibliothèques multimédias. Avec Kometa, vous pouvez porter votre personnalisation au niveau suivant, avec un contrôle granulaire sur les métadonnées, les collections, les superpositions et bien plus encore.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa lit sa configuration à partir de /config/config.yml et le conteneur ne livre que /config/config.yml.template. Copiez le modèle pour config.yml, remplissez les connexions required Plex et TMDb, puis redémarrez le conteneur.",
"Komga is a media server for your comics, mangas, BDs, magazines and eBooks.": "Komga est un serveur multimédia pour vos BD, mangas, BD, magazines et eBooks.",
+ "Kopia Web UI": "Interface web de Kopia",
+ "Kopia Web UI login": "Connexion à l’interface web de Kopia",
"Krita is a professional FREE and open source painting program. It is made by artists that want to see affordable art tools for everyone.": "Krita est un programme professionnel de peinture libre et GRATUIT. Il est fait par des artistes qui veulent voir des outils d'art abordables pour tout le monde.",
"LAN access to the kept containers (stack configuration incomplete):": "Accès au réseau local des conteneurs gardés (configuration incomplète):",
"LAN address applied to the application URLs": "Adresse LAN appliquée aux URL de l'application",
@@ -3894,10 +3919,14 @@
"Log aggregation server for Grafana.": "Serveur d’agrégation de journaux pour Grafana.",
"Log file": "Fichier journal",
"Log2RAM already registered — updating to latest configuration": "Log2RAM déjà enregistré - mise à jour vers la dernière configuration",
+ "Log2RAM could not be downloaded": "Impossible de télécharger Log2RAM",
"Log2RAM installation and configuration completed successfully.": "L'installation et la configuration de Log2RAM se sont terminées avec succès.",
"Log2RAM installation cancelled by user": "Installation de Log2RAM annulée par l'utilisateur",
"Log2RAM installation verification failed. Check /tmp/log2ram_install.log": "La vérification de l'installation de Log2RAM a échoué. Vérifiez /tmp/log2ram_install.log",
"Log2RAM installed successfully": "Log2RAM installé avec succès",
+ "Log2RAM installed:": "Log2RAM installé :",
+ "Log2RAM is removed at the next reboot, after writing /var/log to disk": "Log2RAM est supprimé au prochain redémarrage, après l'écriture de /var/log sur le disque",
+ "Log2RAM keeps /var/log in RAM; the journal stays on disk from the next boot": "Log2RAM garde /var/log en RAM ; le journal reste sur le disque à partir du prochain démarrage",
"Log2RAM keeps the journal in order from the next reboot": "Log2RAM maintient le journal en ordre dès le prochain redémarrage",
"Log2RAM size set to:": "Taille de Log2RAM définie sur :",
"Log2RAM write scheduled every": "Écriture Log2RAM programmée tous les",
@@ -3980,6 +4009,7 @@
"Manage custom backup paths": "Gérer les chemins de sauvegarde personnalisés",
"Manage custom paths (add / remove your folders)": "Gérer les chemins personnalisés (ajouter/supprimer vos dossiers)",
"Manage data and APIs with a modern visual interface.": "Gérez les données et les API avec une interface visuelle moderne.",
+ "Manage database changes, access and workflows.": "Gérez les modifications de base de données, les accès et les flux de travail.",
"Manage installed OCI applications": "Gérer les applications OCI installées",
"Manage local backup target": "Gérer la cible de sauvegarde locale",
"Manage private image registries.\nSaved credentials let ProxMenux access your private images.": "Gérer les registres d’images privés.\nLes informations d'identification enregistrées permettent à ProxMenux d'accéder à vos images privées.",
@@ -4847,6 +4877,9 @@
"Open-source in-memory data store.": "Magasin de données en mémoire libre.",
"Open-source self-hosted SQL IDE.": "IDE SQL auto-organisé open-source.",
"OpenClaw is a personal AI assistant you run on your own devices": "OpenClaw est un assistant d'IA personnel que vous exécutez sur vos propres appareils",
+ "OpenHabitTracker": "OpenHabitTracker",
+ "OpenHabitTracker account": "Compte OpenHabitTracker",
+ "OpenHabitTracker initial username": "Nom d’utilisateur initial d’OpenHabitTracker",
"OpenList": "OpenList",
"OpenObserve administrator": "Administrateur OpenObserve",
"OpenShot Video Editor is an award-winning free and open-source video editor for Linux, Mac, and Windows, and is dedicated to delivering high quality video editing and animation solutions to the world.": "OpenShot Video Editor est un éditeur vidéo gratuit et open-source primé pour Linux, Mac et Windows, et est dédié à fournir des solutions d'édition vidéo et d'animation de haute qualité au monde.",
@@ -5214,6 +5247,7 @@
"Primary upstream DNS server": "Serveur DNS amont principal",
"Privacy-first finance app with envelope budgeting and multi-device sync.": "Privacy-first finance app avec enveloppe budgeting et synchronisation multi-appareils.",
"Privacy-first, self-hosted PDF toolkit": "Boîte à outils PDF pour la première fois en matière de protection de la vie privée",
+ "Privacy-focused, self-hosted web analytics.": "Analyse web auto-hébergée axée sur la confidentialité.",
"Private installation record saved": "Enregistrement d'installation privé enregistré",
"Private network assigned automatically:": "Réseau privé assigné automatiquement:",
"Private network kept, because other containers still use it:": "Réseau privé conservé, car d'autres conteneurs l'utilisent encore :",
@@ -5337,6 +5371,7 @@
"Public UDP port clients connect to": "Les clients du port UDP public se connectent à",
"Public UDP port peers connect to": "Les pairs du port UDP public se connectent à",
"Public URL of phpMyAdmin when it is served behind a reverse proxy": "URL publique de phpMyAdmin quand elle est servie derrière un proxy inversé",
+ "Public URL used to access Baserow (for example https://baserow.example.com)": "URL publique pour accéder à Baserow (par exemple https://baserow.example.com)",
"Public address clients connect to (vpn.example.com or a public IP)": "Les clients d'adresses publiques se connectent à (vpn.example.com ou à un IP public)",
"Public address peers connect to, or auto to detect it (vpn.example.com)": "Pairs d'adresse publique se connectent, ou auto pour le détecter (vpn.example.com)",
"Publish each directory from Resources: open it, switch on SMB, NFS or both, and save.": "Publiez chaque répertoire à partir des ressources : ouvrez-le, activez SMB, NFS ou les deux et enregistrez.",
@@ -5362,6 +5397,7 @@
"RAM in MiB": "RAM en MiB",
"REPAIR SUMMARY": "RÉSUMÉ DE LA RÉPARATION",
"REQUIREMENTS:": "EXIGENCES:",
+ "REST API Basic Authentication user": "Utilisateur de l’authentification HTTP Basic de l’API REST",
"ROCm does not support this GPU officially. The application can use it by presenting it as another generation of its family: it is faster than the CPU, but under load the GPU can stop responding, and then the application does not answer until its container is restarted. The ROCm image is also larger than the others.": "ROCm ne prend pas officiellement en charge ce GPU.L'application peut l'utiliser en le présentant comme une autre génération de sa famille : il est plus rapide que le CPU, mais sous charge le GPU peut cesser de répondre, et alors l'application ne répond plus jusqu'au redémarrage de son conteneur. L'image ROCm est également plus grande que les autres.",
"ROCm requires /dev/kfd on the host": "ROCm nécessite /dev/kfd sur l'hôte",
"ROCm requires the render device of an AMD GPU": "ROCm nécessite le périphérique de rendu d'un AMD GPU",
@@ -5672,6 +5708,7 @@
"Repositories switched to no-subscription": "Les référentiels sont passés au sans abonnement",
"Repository check failed. NVIDIA installation stopped.": "La vérification du référentiel a échoué. L'installation de NVIDIA s'est arrêtée.",
"Repository check failed. Update stopped.": "La vérification du référentiel a échoué. La mise à jour s'est arrêtée.",
+ "Repository encryption password": "Mot de passe de chiffrement du dépôt",
"Repository helper unavailable. NVIDIA installation stopped.": "Assistant du référentiel indisponible. L'installation de NVIDIA s'est arrêtée.",
"Repository policy returned an unexpected result.": "La stratégie du référentiel a renvoyé un résultat inattendu.",
"Repository ready.": "Référentiel prêt.",
@@ -5789,6 +5826,7 @@
"Root shell/profile config": "Configuration du shell/profil racine",
"Root user on the PVE host (default 'root'):": "Utilisateur root sur l'hôte PVE ('root' par défaut) :",
"Rootfs size in GB": "Taille des racines en GB",
+ "Rotate and write /var/log to disk when it passes 85% of its size?": "Effectuer la rotation de /var/log et l'écrire sur le disque lorsqu'il dépasse 85 % de sa taille ?",
"Rotate the recovery passphrase": "Faites pivoter la phrase secrète de récupération",
"Routing Information": "Informations d'acheminement",
"Routing Table": "Table de routage",
@@ -6261,6 +6299,7 @@
"Set ownership and permissions:": "Définir la propriété et les autorisations :",
"Set the initial Technitium DNS administrator password": "Définir le mot de passe initial de l’administrateur DNS Technitium",
"Set this disk as the primary boot disk?": "Définir ce disque comme disque de démarrage principal ?",
+ "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Définissez la valeur sur 1 uniquement pour un réseau local privé utilisant exclusivement HTTP ; sinon, utilisez HTTPS.",
"Set up custom MOTD banner": "Configurer une bannière MOTD personnalisée",
"Set up the apex group and udev rules": "Configurer le groupe apex et les règles udev",
"Set up time synchronization and entropy generation": "Configurer la synchronisation temporelle et la génération d'entropie",
@@ -6965,6 +7004,7 @@
"The domain must resolve to the public address of this network before the certificate can be issued.": "Le domaine doit se résoudre à l'adresse publique de ce réseau avant la délivrance du certificat.",
"The download client still needs to be configured.": "Le client de téléchargement doit encore être configuré.",
"The download stopped progressing; cancelling this attempt.": "Le téléchargement a cessé de progresser; annuler cette tentative.",
+ "The downloaded Log2RAM does not match the expected release": "Le Log2RAM téléchargé ne correspond pas à la version attendue",
"The downloaded image does not match its manifest": "L'image téléchargée ne correspond pas à son manifeste",
"The downloaded image is corrupt:": "L'image téléchargée est corrompue :",
"The dpkg package database is clean.": "La base de données du package dpkg est propre.",
@@ -7114,6 +7154,7 @@
"The oci-compose is incomplete: it does not say which image to install.": "L'oci-compose est incomplet : il n'indique pas quelle image installer.",
"The oci-compose is not valid JSON:": "L'oci-compose n'est pas un JSON valide :",
"The official Tandoor startup executable is missing": "La startup officielle Tandoor exécutable est manquante",
+ "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Les services Compose officiels d’Umami et de PostgreSQL ne demandent ni le mode privilégié ni un assouplissement de l’isolation de l’hôte.",
"The official inventory contains no NVIDIA devices": "L'inventaire officiel ne contient pas de dispositifs NVIDIA",
"The official inventory contains no NVIDIA driver components": "L'inventaire officiel ne contient pas de composants conducteurs NVIDIA",
"The official startup cannot be reproduced": "Le démarrage officiel ne peut pas être reproduit",
@@ -7534,6 +7575,7 @@
"Total routes": "Total des itinéraires",
"Total size:": "Taille totale :",
"Toxiproxy": "Toxiproxy",
+ "Track movies, television, anime and games you want to enjoy.": "Suivez les films, séries, animés et jeux vidéo que vous souhaitez découvrir.",
"Track subscriptions and recurring expenses.": "Suivi des abonnements et des dépenses récurrentes.",
"Track your music listening history on your own server.": "Historique d’écoute musicale hébergé sur votre propre serveur.",
"Track your reading, books and history in a private library.": "Suivre ses lectures, ses livres et son historique dans une bibliothèque privée.",
@@ -7613,6 +7655,7 @@
"Uninstalling Ceph...": "Désinstallation de Ceph...",
"Uninstalling Fastfetch...": "Désinstallation de Fastfetch...",
"Uninstalling Figurine...": "Désinstallation de Figurine...",
+ "Uninstalling Log2RAM...": "Désinstallation de Log2RAM...",
"Uninstalling amdgpu_top...": "Désinstallation d'amdgpu_top...",
"Uninstalling current NVIDIA driver before installing new version": "Désinstallation du pilote NVIDIA actuel avant d'installer la nouvelle version",
"Uninstalling intel-gpu-tools...": "Désinstallation des outils Intel-Gpu...",
@@ -8091,6 +8134,7 @@
"Warning: both VMs have autostart enabled (onboot=1).": "Attention : les deux machines virtuelles ont le démarrage automatique activé (onboot=1).",
"Warnings": "Avertissements",
"Watch history, ratings and watchlists synced between media servers and trackers.": "Historique, notes et listes de suivi synchronisés entre serveurs multimédias et trackers.",
+ "Watcharr": "Watcharr",
"Watchdog": "Surveillance",
"Watchdog (off): restart the application when it crashes": "Surveillance (désactivée) : redémarrer l'application lorsqu'elle plante",
"Watchdog (on): restart the application when it crashes": "Surveillance (activée) : redémarrer l'application lorsqu'elle plante",
@@ -8868,10 +8912,5 @@
"⚠ Disk data will NOT be erased.": "⚠ Les données du disque ne seront PAS effacées.",
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Le disque sera démonté et supprimé de /etc/fstab.",
"⚠ The /etc/fstab entry will be removed.": "⚠ L'entrée /etc/fstab sera supprimée.",
- "⚠ The disk will be unmounted.": "⚠ Le disque sera démonté.",
- "Initial Umami administrator": "Administrateur initial d’Umami",
- "Privacy-focused, self-hosted web analytics.": "Analyse web auto-hébergée axée sur la confidentialité.",
- "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Les services Compose officiels d’Umami et de PostgreSQL ne demandent ni le mode privilégié ni un assouplissement de l’isolation de l’hôte.",
- "A self-hosted, no-code database and application platform.": "Plateforme auto-hébergée de bases de données et d’applications sans code.",
- "Public URL used to access Baserow (for example https://baserow.example.com)": "URL publique pour accéder à Baserow (par exemple https://baserow.example.com)"
+ "⚠ The disk will be unmounted.": "⚠ Le disque sera démonté."
}
diff --git a/lang/it.json b/lang/it.json
index 95478fe9..6b1c6089 100644
--- a/lang/it.json
+++ b/lang/it.json
@@ -47,6 +47,7 @@
"A GPS tracking platform with device management and fleet reports.": "Una piattaforma di tracciamento GPS con gestione dei dispositivi e report della flotta.",
"A GTK Broadway web UI for libvirt and virt-manager.": "Un'interfaccia web GTK Broadway per libvirt e virt-manager.",
"A Personal Relationship Management tool to help you document your social life.": "Uno strumento di gestione delle relazioni personali per documentare la tua vita sociale.",
+ "A REST backend for restic backups; it has no web interface.": "Un backend REST per i backup di restic, senza interfaccia web.",
"A VirtIO ISO already exists. Do you want to overwrite it?": "Esiste già un’immagine ISO VirtIO. Vuoi sovrascriverla?",
"A ZFS pool with this name already exists.": "Esiste già un pool ZFS con questo nome.",
"A ZFS pool with this name already exists:": "Esiste già un pool ZFS con questo nome:",
@@ -94,6 +95,7 @@
"A lightweight dashboard that brings feeds, services and useful widgets together.": "Una dashboard leggera che riunisce feed, servizi e widget utili.",
"A lightweight hub for monitoring servers and containers.": "Un hub leggero per monitorare server e container.",
"A lightweight self-hosted Kanban board.": "Una bacheca Kanban leggera e self-hosted.",
+ "A lightweight self-hosted URL shortener with SQLite storage.": "Un accorciatore di URL leggero e self-hosted con archivio SQLite.",
"A lightweight self-hosted music server with Subsonic API support.": "Un server musicale leggero e self-hosted, compatibile con l'API Subsonic.",
"A lightweight self-hosted pastebin for text and files.": "Un servizio leggero e self-hosted per condividere testo e file.",
"A lightweight static homepage for organizing self-hosted services.": "Una homepage statica e leggera per organizzare i servizi self-hosted.",
@@ -124,6 +126,7 @@
"A pending operation exists for": "Operazione in sospeso per il",
"A pending stack assembly already exists; it is not overwritten": "Esiste già un assemblaggio dello stack in sospeso; non verrà sovrascritto",
"A personal homepage for your apps and dashboards.": "Una homepage personale per le tue app e dashboard.",
+ "A personal, self-hosted microblog and timeline.": "Un microblog e una cronologia personali, self-hosted.",
"A previous NVIDIA refresh is pending review": "Un precedente aggiornamento NVIDIA è in attesa di verifica",
"A previous VFIO passthrough configuration was detected for the following NVIDIA GPU(s):": "È stata rilevata una configurazione passthrough VFIO precedente per le seguenti GPU NVIDIA:",
"A privacy-first, open-source platform for knowledge management and collaboration.": "Una piattaforma open source attenta alla privacy per la gestione della conoscenza e la collaborazione.",
@@ -147,15 +150,18 @@
"A self-hosted Q&A and knowledge-sharing platform.": "Una piattaforma self-hosted di domande e risposte, con condivisione delle conoscenze.",
"A self-hosted RSS reader inspired by Google Reader.": "Lettore RSS self-hosted ispirato a Google Reader.",
"A self-hosted app for organizing recurring tasks and household chores, with data stored in SQLite.": "Applicazione self-hosted per organizzare attività e faccende ricorrenti, con archiviazione SQLite.",
+ "A self-hosted app for organizing tabletop role-playing campaigns.": "Un’app self-hosted per organizzare campagne di giochi di ruolo da tavolo.",
"A self-hosted bookmark manager and web archiver.": "Un gestore di segnalibri e archiviatore web self-hosted.",
"A self-hosted bookmark manager.": "Gestore di segnalibri self-hosted.",
"A self-hosted file sharing service with expiring downloads.": "Servizio self-hosted di condivisione file con download a scadenza.",
"A self-hosted link-in-bio page.": "Pagina link-in-bio self-hosted.",
"A self-hosted read-it-later application.": "Un'applicazione self-hosted per salvare articoli da leggere in seguito.",
"A self-hosted task manager for planning work and tracking time.": "Un gestore di attività self-hosted per pianificare il lavoro e tenere traccia del tempo.",
+ "A self-hosted tracker for habits, tasks, notes and goals.": "Un tracker self-hosted per abitudini, attività, note e obiettivi.",
"A self-hosted vehicle maintenance and fuel log.": "Un registro self-hosted per la manutenzione dei veicoli e il consumo di carburante.",
"A self-hosted workflow scheduler with a web interface.": "Pianificatore di workflow self-hosted con interfaccia web.",
"A self-hosted, goal-free habit tracking tool.": "Uno strumento self-hosted per monitorare le abitudini senza obiettivi prefissati.",
+ "A self-hosted, no-code database and application platform.": "Piattaforma self-hosted per creare database e applicazioni senza scrivere codice.",
"A self-improving AI agent with memory, skills, messaging, and a web dashboard.": "Un agente IA che si migliora autonomamente, con memoria, competenze, messaggistica e una dashboard web.",
"A server reboot is recommended for all changes to take full effect.": "Si consiglia di riavviare il server affinché tutte le modifiche abbiano pieno effetto.",
"A shared directory was replaced during the installation": "Una directory condivisa è stata sostituita durante l'installazione",
@@ -276,6 +282,7 @@
"Active ZRAM algorithm, capacity or swap sources did not match the profile": "L'algoritmo ZRAM attivo, la capacità o le sorgenti di swap non corrispondono al profilo",
"Active exports:": "Esportazioni attive:",
"Active session:": "Sessione attiva:",
+ "Activepieces": "Activepieces",
"Actual device path on the host": "Percorso effettivo del dispositivo sull'host",
"Adaptation file too large": "File di adattamento troppo grande",
"Adaptation file with unexpected permissions or owner": "File di adattamento con proprietario o permessi imprevisti",
@@ -783,6 +790,8 @@
"Build and install the gasket and apex kernel modules (DKMS)": "Compila e installa i moduli kernel gasket e apex (DKMS)",
"Build dependencies installed.": "Dipendenze di compilazione installate.",
"Build dynamic websites using SQL files.": "Crea siti web dinamici usando file SQL.",
+ "Build reports and dashboards from your data.": "Crea report e dashboard a partire dai tuoi dati.",
+ "Build visual automations for personal and hobby use.": "Crea automazioni visive per uso personale e per i tuoi hobby.",
"Build your personal knowledge base with TriliumNext Notes": "Crea la tua base di conoscenze personale con TriliumNext Notes",
"Bulk generation requires GITHUB_TOKEN to obtain an immutable revision per application": "La generazione in blocco richiede GITHUB_TOKEN per ottenere una revisione immutabile per ogni applicazione",
"Business & ERP": "Business e ERP",
@@ -827,6 +836,7 @@
"Calibre-web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. It is also possible to integrate google drive and edit metadata and your calibre library through the app itself.": "Calibre-web è un’app web con un’interfaccia semplice per sfogliare, leggere e scaricare eBook da un database Calibre esistente. Permette anche di integrare Google Drive e modificare i metadati e la libreria Calibre direttamente dall’app.",
"Calligra is an office and graphic art suite by KDE. It is available for desktop PCs, tablet computers, and smartphones. It contains applications for word processing, spreadsheets, presentation, vector graphics, and editing databases.": "Calligra è una suite KDE per l'ufficio e la grafica. È disponibile per PC, tablet e smartphone e comprende applicazioni per elaborare testi, fogli di calcolo, presentazioni, grafica vettoriale e database.",
"Calnode": "Calnode",
+ "Campfire first-run setup": "Configurazione iniziale di Campfire",
"Cancel": "Annulla",
"Cancel restore": "Annulla ripristino",
"Cancel this setup": "Annulla questa configurazione",
@@ -974,6 +984,8 @@
"Checklist post-upgrade finished. Warnings:": "Checklist post-aggiornamento completata. Avvisi:",
"Checklist pre-check finished. Warnings:": "Controllo preliminare completato. Avvertenze:",
"Checks for LVM and storage issues": "Verifica problemi LVM e storage",
+ "Chhoto URL": "Chhoto URL",
+ "Chhoto URL administrator password": "Password amministratore di Chhoto URL",
"Choose": "Scegli",
"Choose BIOS Type": "Scegli il tipo di BIOS",
"Choose BIOS type": "Scegli tipo BIOS",
@@ -1192,6 +1204,7 @@
"Configured during installation": "Configurato durante l’installazione",
"Configured import disk %s as %s": "Disco importato %s configurato come %s",
"Configured loader disk as ide0": "Disco del loader configurato come ide0",
+ "Configured username": "Nome utente configurato",
"Configured virtual disk as %s, %sGB on %s": "Disco virtuale configurato come %s, %sGB su %s",
"Configuring AMD GPU passthrough...": "Configurazione del passthrough GPU AMD...",
"Configuring AMD softdep...": "Configurazione del softdep AMD...",
@@ -1601,6 +1614,7 @@
"Create and stream custom live TV channels from your media library.": "Crea e trasmetti canali TV in diretta personalizzati dalla tua raccolta multimediale.",
"Create credentials file (recommended):": "Crea un file delle credenziali (consigliato):",
"Create directory": "Crea directory",
+ "Create encrypted, deduplicated backups and restore files.": "Crea backup cifrati e deduplicati e ripristina i file.",
"Create export directory:": "Crea directory di esportazione:",
"Create mount point:": "Crea punto di montaggio:",
"Create new directory in /mnt": "Crea nuova directory in /mnt",
@@ -1611,9 +1625,11 @@
"Create share directory:": "Crea directory condivisa:",
"Create shared directory:": "Crea directory condivisa:",
"Create this LXC in privileged mode?": "Creare questo LXC in modalità privilegiata?",
+ "Create with create_user in the container console": "Crea l’utente con create_user nella console del container",
"Create, send and sign documents with a self-hosted web app.": "Crea, invia e firma documenti con un'app web self-hosted.",
"Created common remapped user": "Creato utente rimappato comune",
"Created directory on host:": "Directory creata sull'host:",
+ "Created during first-run setup": "Creato durante la configurazione iniziale",
"Created persistent names for": "Creati nomi persistenti per",
"Created remapped user": "Creato utente rimappato",
"Created sharedfiles group (GID: 101000)": "Gruppo sharedfiles creato (GID: 101000)",
@@ -2127,6 +2143,7 @@
"Each LUN will appear as a block device assignable to VMs.": "Ogni LUN apparirà come un dispositivo a blocchi assegnabile alle VM.",
"Each peer gets its configuration and its QR code inside the container: /config/peer1/peer1.conf and /config/peer1/peer1.png, or /config/peer_/peer_.conf when names were given.": "Ogni peer riceve la propria configurazione e il proprio codice QR nel container: /config/peer1/peer1.conf e /config/peer1/peer1.png oppure /config/peer_/peer_.conf se sono stati forniti nomi.",
"Ebook and audiobook collection manager for Usenet and BitTorrent users.": "Gestore di raccolte di ebook e audiolibri per utenti Usenet e BitTorrent.",
+ "Ech0": "Ech0",
"Eden is an experimental open-source emulator for the Nintendo Switch, built with performance and stability in mind.": "Eden è un emulatore sperimentale open source per Nintendo Switch, progettato per offrire prestazioni e stabilità.",
"Edge TPU runtime installed.": "Runtime Edge TPU installato.",
"Edit": "Modifica",
@@ -2136,6 +2153,7 @@
"Edit the VM machine type to q35 and try again.": "Modifica il tipo di macchina VM su q35 e riprova.",
"Edition:": "Edizione:",
"Email address for certificate expiry notices (required by ZeroSSL)": "Email per avvisi di scadenza del certificato (richiesta da ZeroSSL)",
+ "Email address for the OpenHabitTracker account": "Indirizzo e-mail dell’account OpenHabitTracker",
"Email address of the LibreDB Studio administrator": "Email dell’amministratore LibreDB Studio",
"Email address of the NetBox admin account": "Email dell’account amministratore NetBox",
"Email address of the admin account": "Indirizzo email dell'account amministratore",
@@ -2299,6 +2317,7 @@
"Enter the BORG REPOKEY passphrase for target:": "Inserisci la passphrase BORG REPOKEY per la destinazione:",
"Enter the Samba username:": "Inserisci il nome utente Samba:",
"Enter the VMID for the new VM:": "Inserisci il VMID per la nuova VM:",
+ "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Inserisci l’URL di base che client e provider webhook useranno per raggiungere questa istanza di Activepieces.",
"Enter the container terminal:": "Accedi al terminale del container:",
"Enter the full path to the directory containing disk images:": "Inserisci il percorso completo della directory contenente le immagini del disco:",
"Enter the full path to the host directory:": "Inserisci il percorso completo della directory host:",
@@ -2668,7 +2687,9 @@
"Firewall allows port": "Il firewall consente la porta",
"Firewall settings": "Impostazioni firewall",
"Firmware :": "Firmware:",
+ "First Ech0 account (becomes Owner)": "Primo account Ech0 (diventa proprietario)",
"First Jelu administrator": "Primo amministratore Jelu",
+ "First Watcharr account (becomes administrator)": "Primo account Watcharr (diventa amministratore)",
"First complete DSM setup and verify Web UI/SSH access.": "Completa prima la configurazione DSM e verifica l'accesso web/SSH.",
"First complete ZimaOS setup and verify remote access (web/SSH).": "Completa prima la configurazione ZimaOS e verifica l'accesso remoto (web/SSH).",
"First install the GPU drivers inside the guest and verify remote access (RDP/SSH).": "Installa prima i driver GPU nel guest e verifica l'accesso remoto (RDP/SSH).",
@@ -3306,6 +3327,7 @@
"Initial Redmine administrator": "Primo amministratore Redmine",
"Initial Stirling-PDF account": "Account iniziale di Stirling-PDF",
"Initial Tandoor administrator": "Amministratore iniziale Tandoor",
+ "Initial Umami administrator": "Amministratore iniziale di Umami",
"Initial administrator created:": "Amministratore iniziale creato:",
"Initial administrator user": "Utente amministratore iniziale",
"Initializing Borg repository if needed...": "Inizializzazione del repository Borg, se necessario...",
@@ -3404,6 +3426,7 @@
"Installing Fail2Ban...": "Installazione Fail2Ban...",
"Installing Git as a prerequisite...": "Installazione Git come prerequisito...",
"Installing Intel VA-API drivers in container...": "Installazione dei driver Intel VA-API nel container...",
+ "Installing Log2RAM": "Installazione di Log2RAM",
"Installing Log2RAM from GitHub...": "Installazione di Log2RAM da GitHub...",
"Installing Log2RAM from source...": "Installazione di Log2RAM dal sorgente...",
"Installing NFS Client in LXC": "Installazione del client NFS in LXC",
@@ -3753,6 +3776,8 @@
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa è uno strumento che ti offre un controllo completo sulle tue librerie multimediali. Permette di personalizzare nel dettaglio metadati, raccolte, sovrimpressioni e molto altro.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa legge la configurazione da /config/config.yml, ma il container include solo /config/config.yml.template. Copia il modello in config.yml, configura le connessioni necessarie a Plex e TMDb, quindi riavvia il container.",
"Komga is a media server for your comics, mangas, BDs, magazines and eBooks.": "Komga è un server multimediale per fumetti, manga, bande dessinée, riviste ed e-book.",
+ "Kopia Web UI": "Interfaccia web di Kopia",
+ "Kopia Web UI login": "Accesso all’interfaccia web di Kopia",
"Krita is a professional FREE and open source painting program. It is made by artists that want to see affordable art tools for everyone.": "Krita è un programma professionale, gratuito e open source per dipingere. È creato da artisti che vogliono rendere gli strumenti artistici accessibili a tutti.",
"LAN access to the kept containers (stack configuration incomplete):": "Accesso LAN ai container conservati (configurazione stack incompleta):",
"LAN address applied to the application URLs": "Indirizzo LAN applicato agli URL dell'applicazione",
@@ -3894,10 +3919,14 @@
"Log aggregation server for Grafana.": "Server di aggregazione dei log per Grafana.",
"Log file": "File di registro",
"Log2RAM already registered — updating to latest configuration": "Log2RAM già registrato: aggiornamento all'ultima configurazione",
+ "Log2RAM could not be downloaded": "Impossibile scaricare Log2RAM",
"Log2RAM installation and configuration completed successfully.": "Installazione e configurazione Log2RAM completate.",
"Log2RAM installation cancelled by user": "Installazione Log2RAM annullata dall'utente",
"Log2RAM installation verification failed. Check /tmp/log2ram_install.log": "Verifica installazione Log2RAM non riuscita. Controlla /tmp/log2ram_install.log",
"Log2RAM installed successfully": "Log2RAM installato correttamente",
+ "Log2RAM installed:": "Log2RAM installato:",
+ "Log2RAM is removed at the next reboot, after writing /var/log to disk": "Log2RAM viene rimosso al prossimo riavvio, dopo aver scritto /var/log su disco",
+ "Log2RAM keeps /var/log in RAM; the journal stays on disk from the next boot": "Log2RAM mantiene /var/log in RAM; il journal resta su disco dal prossimo avvio",
"Log2RAM keeps the journal in order from the next reboot": "Log2RAM mantiene in ordine il journal di sistema dal prossimo riavvio",
"Log2RAM size set to:": "Dimensione Log2RAM impostata su:",
"Log2RAM write scheduled every": "Scrittura Log2RAM programmata ogni",
@@ -3980,6 +4009,7 @@
"Manage custom backup paths": "Gestisci percorsi di backup personalizzati",
"Manage custom paths (add / remove your folders)": "Gestisci percorsi personalizzati (aggiungi/rimuovi le tue cartelle)",
"Manage data and APIs with a modern visual interface.": "Gestisci dati e API con un'interfaccia visiva moderna.",
+ "Manage database changes, access and workflows.": "Gestisci le modifiche al database, gli accessi e i flussi di lavoro.",
"Manage installed OCI applications": "Gestisci le applicazioni OCI installate",
"Manage local backup target": "Gestisci la destinazione di backup locale",
"Manage private image registries.\nSaved credentials let ProxMenux access your private images.": "Gestisci i registri di immagini private.\nLe credenziali salvate consentono a ProxMenux di accedere alle tue immagini private.",
@@ -4847,6 +4877,9 @@
"Open-source in-memory data store.": "Archivio dati in memoria open source.",
"Open-source self-hosted SQL IDE.": "IDE SQL open source ospitabile sul proprio server.",
"OpenClaw is a personal AI assistant you run on your own devices": "OpenClaw è un assistente IA personale che puoi eseguire sui tuoi dispositivi",
+ "OpenHabitTracker": "OpenHabitTracker",
+ "OpenHabitTracker account": "Account OpenHabitTracker",
+ "OpenHabitTracker initial username": "Nome utente iniziale di OpenHabitTracker",
"OpenList": "OpenList",
"OpenObserve administrator": "Amministratore OpenObserve",
"OpenShot Video Editor is an award-winning free and open-source video editor for Linux, Mac, and Windows, and is dedicated to delivering high quality video editing and animation solutions to the world.": "OpenShot Video Editor è un editor video gratuito e open source pluripremiato per Linux, Mac e Windows, dedicato alla creazione di video e animazioni di alta qualità.",
@@ -5214,6 +5247,7 @@
"Primary upstream DNS server": "Server DNS upstream principale",
"Privacy-first finance app with envelope budgeting and multi-device sync.": "App finanziaria attenta alla privacy con gestione del budget a buste e sincronizzazione tra dispositivi.",
"Privacy-first, self-hosted PDF toolkit": "Strumenti PDF self-hosted orientati alla privacy",
+ "Privacy-focused, self-hosted web analytics.": "Analisi web self-hosted, attenta alla privacy.",
"Private installation record saved": "Record privato di installazione salvato",
"Private network assigned automatically:": "Rete privata assegnata automaticamente:",
"Private network kept, because other containers still use it:": "Rete privata mantenuta, perché altri contenitori la utilizzano ancora:",
@@ -5337,6 +5371,7 @@
"Public UDP port clients connect to": "Porta UDP pubblica a cui si connettono i client",
"Public UDP port peers connect to": "Porta UDP pubblica a cui si connettono i peer",
"Public URL of phpMyAdmin when it is served behind a reverse proxy": "URL pubblico di phpMyAdmin quando viene servito dietro un proxy inverso",
+ "Public URL used to access Baserow (for example https://baserow.example.com)": "URL pubblico usato per accedere a Baserow (ad esempio https://baserow.example.com)",
"Public address clients connect to (vpn.example.com or a public IP)": "Indirizzo pubblico a cui si connettono i client (vpn.example.com o un IP pubblico)",
"Public address peers connect to, or auto to detect it (vpn.example.com)": "Indirizzo pubblico a cui si connettono i peer, oppure auto per rilevarlo (vpn.example.com)",
"Publish each directory from Resources: open it, switch on SMB, NFS or both, and save.": "Pubblica ciascuna directory da Risorse: aprila, attiva SMB, NFS o entrambi e salva.",
@@ -5362,6 +5397,7 @@
"RAM in MiB": "RAM in MiB",
"REPAIR SUMMARY": "RIEPILOGO RIPARAZIONE",
"REQUIREMENTS:": "REQUISITI:",
+ "REST API Basic Authentication user": "Utente per l’autenticazione di base dell’API REST",
"ROCm does not support this GPU officially. The application can use it by presenting it as another generation of its family: it is faster than the CPU, but under load the GPU can stop responding, and then the application does not answer until its container is restarted. The ROCm image is also larger than the others.": "ROCm non supporta ufficialmente questa GPU. L’applicazione può usarla presentandola come un’altra generazione della stessa famiglia: è più veloce della CPU, ma sotto carico la GPU può smettere di rispondere. In tal caso l’applicazione non risponde finché il suo container non viene riavviato. L’immagine ROCm è anche più grande delle altre.",
"ROCm requires /dev/kfd on the host": "ROCm richiede /dev/kfd sull'host",
"ROCm requires the render device of an AMD GPU": "ROCm richiede il dispositivo di rendering di una GPU AMD",
@@ -5672,6 +5708,7 @@
"Repositories switched to no-subscription": "I repository sono passati alla modalità senza abbonamento",
"Repository check failed. NVIDIA installation stopped.": "Controllo del repository non riuscito. L'installazione di NVIDIA è stata interrotta.",
"Repository check failed. Update stopped.": "Controllo del repository non riuscito. Aggiornamento interrotto.",
+ "Repository encryption password": "Password di cifratura del repository",
"Repository helper unavailable. NVIDIA installation stopped.": "Funzione di gestione dei repository non disponibile. L’installazione di NVIDIA è stata interrotta.",
"Repository policy returned an unexpected result.": "La policy del repository ha restituito un risultato imprevisto.",
"Repository ready.": "Repository pronto.",
@@ -5789,6 +5826,7 @@
"Root shell/profile config": "Configurazione shell/profilo root",
"Root user on the PVE host (default 'root'):": "Utente root sull'host PVE (default 'root'):",
"Rootfs size in GB": "Dimensione rootfs in GB",
+ "Rotate and write /var/log to disk when it passes 85% of its size?": "Ruotare e scrivere /var/log su disco quando supera l'85% della sua dimensione?",
"Rotate the recovery passphrase": "Sostituisci la passphrase di ripristino",
"Routing Information": "Informazioni di instradamento",
"Routing Table": "Tabella di instradamento",
@@ -6261,6 +6299,7 @@
"Set ownership and permissions:": "Imposta proprietà e autorizzazioni:",
"Set the initial Technitium DNS administrator password": "Imposta la password iniziale dell'amministratore DNS di Technitium",
"Set this disk as the primary boot disk?": "Impostare questo disco come disco di avvio principale?",
+ "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Imposta il valore su 1 solo per una LAN privata che usa esclusivamente HTTP; altrimenti usa HTTPS.",
"Set up custom MOTD banner": "Imposta un banner MOTD personalizzato",
"Set up the apex group and udev rules": "Imposta il gruppo apex e le regole udev",
"Set up time synchronization and entropy generation": "Imposta sincronizzazione dell’ora e generazione di entropia",
@@ -6966,6 +7005,7 @@
"The domain must resolve to the public address of this network before the certificate can be issued.": "Il dominio deve risolversi nell'indirizzo pubblico di questa rete prima che il certificato possa essere emesso.",
"The download client still needs to be configured.": "Il client di download deve ancora essere configurato.",
"The download stopped progressing; cancelling this attempt.": "Il download non avanza più; annullamento del tentativo.",
+ "The downloaded Log2RAM does not match the expected release": "Il Log2RAM scaricato non corrisponde alla versione prevista",
"The downloaded image does not match its manifest": "L’immagine scaricata non corrisponde al manifest",
"The downloaded image is corrupt:": "L'immagine scaricata è danneggiata:",
"The dpkg package database is clean.": "Il database dei pacchetti dpkg è pulito.",
@@ -7115,6 +7155,7 @@
"The oci-compose is incomplete: it does not say which image to install.": "L'oci-compose è incompleto: non indica quale immagine installare.",
"The oci-compose is not valid JSON:": "L'oci-compose non è un JSON valido:",
"The official Tandoor startup executable is missing": "Manca l'eseguibile ufficiale per l'avvio di Tandoor",
+ "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "I servizi Compose ufficiali di Umami e PostgreSQL non richiedono l'uso della modalità privilegiata né l'allentamento dell'isolamento dell'host.",
"The official inventory contains no NVIDIA devices": "L'inventario ufficiale non contiene dispositivi NVIDIA",
"The official inventory contains no NVIDIA driver components": "L'inventario ufficiale non contiene componenti driver NVIDIA",
"The official startup cannot be reproduced": "Non è possibile riprodurre l'avvio previsto dall'immagine ufficiale",
@@ -7535,6 +7576,7 @@
"Total routes": "Rotte totali",
"Total size:": "Dimensione totale:",
"Toxiproxy": "Toxiproxy",
+ "Track movies, television, anime and games you want to enjoy.": "Tieni traccia di film, serie TV, anime e videogiochi che vuoi guardare o provare.",
"Track subscriptions and recurring expenses.": "Tieni traccia degli abbonamenti e delle spese ricorrenti.",
"Track your music listening history on your own server.": "Tieni traccia della cronologia di ascolto musicale sul tuo server.",
"Track your reading, books and history in a private library.": "Tieni traccia delle letture, dei libri e della cronologia in una biblioteca privata.",
@@ -7614,6 +7656,7 @@
"Uninstalling Ceph...": "Disinstallazione Ceph...",
"Uninstalling Fastfetch...": "Disinstallazione Fastfetch...",
"Uninstalling Figurine...": "Disinstallazione Figurine...",
+ "Uninstalling Log2RAM...": "Disinstallazione di Log2RAM...",
"Uninstalling amdgpu_top...": "Disinstallazione amdgpu_top...",
"Uninstalling current NVIDIA driver before installing new version": "Disinstallazione del driver NVIDIA corrente prima di installare la nuova versione",
"Uninstalling intel-gpu-tools...": "Disinstallazione intel-gpu-tools...",
@@ -8092,6 +8135,7 @@
"Warning: both VMs have autostart enabled (onboot=1).": "Attenzione: entrambe le VM hanno l'avvio automatico abilitato (onboot=1).",
"Warnings": "Avvisi",
"Watch history, ratings and watchlists synced between media servers and trackers.": "Cronologia, valutazioni e watchlist sincronizzate tra server multimediali e tracker.",
+ "Watcharr": "Watcharr",
"Watchdog": "Watchdog",
"Watchdog (off): restart the application when it crashes": "Watchdog (disattivato): riavvio in caso di arresto anomalo",
"Watchdog (on): restart the application when it crashes": "Watchdog (attivato): riavvio in caso di arresto anomalo",
@@ -8869,10 +8913,5 @@
"⚠ Disk data will NOT be erased.": "⚠ I dati del disco NON verranno cancellati.",
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Il disco verrà smontato e rimosso da /etc/fstab.",
"⚠ The /etc/fstab entry will be removed.": "⚠ La voce /etc/fstab verrà rimossa.",
- "⚠ The disk will be unmounted.": "⚠ Il disco verrà smontato.",
- "Initial Umami administrator": "Amministratore iniziale di Umami",
- "Privacy-focused, self-hosted web analytics.": "Analisi web self-hosted, attenta alla privacy.",
- "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "I servizi Compose ufficiali di Umami e PostgreSQL non richiedono l'uso della modalità privilegiata né l'allentamento dell'isolamento dell'host.",
- "A self-hosted, no-code database and application platform.": "Piattaforma self-hosted per creare database e applicazioni senza scrivere codice.",
- "Public URL used to access Baserow (for example https://baserow.example.com)": "URL pubblico usato per accedere a Baserow (ad esempio https://baserow.example.com)"
+ "⚠ The disk will be unmounted.": "⚠ Il disco verrà smontato."
}
diff --git a/lang/pt.json b/lang/pt.json
index 4efa6559..b1db5161 100644
--- a/lang/pt.json
+++ b/lang/pt.json
@@ -47,6 +47,7 @@
"A GPS tracking platform with device management and fleet reports.": "Uma plataforma de rastreamento GPS com gestão de dispositivos e relatórios de frota.",
"A GTK Broadway web UI for libvirt and virt-manager.": "Um GTK Broadway web UI para libvirt e virt-manager.",
"A Personal Relationship Management tool to help you document your social life.": "Uma relação pessoal Ferramenta de gerenciamento para ajudá-lo a documentar sua vida social.",
+ "A REST backend for restic backups; it has no web interface.": "Um backend REST para cópias de segurança do restic, sem interface web.",
"A VirtIO ISO already exists. Do you want to overwrite it?": "Já existe uma ISO do VirtIO. Você quer sobrescrevê-lo?",
"A ZFS pool with this name already exists.": "Já existe um pool ZFS com esse nome.",
"A ZFS pool with this name already exists:": "Já existe um pool ZFS com este nome:",
@@ -94,6 +95,7 @@
"A lightweight dashboard that brings feeds, services and useful widgets together.": "Um painel leve que reúne feeds, serviços e widgets úteis.",
"A lightweight hub for monitoring servers and containers.": "Um painel leve para monitorizar servidores e contentores.",
"A lightweight self-hosted Kanban board.": "Um quadro Kanban leve e autoalojado.",
+ "A lightweight self-hosted URL shortener with SQLite storage.": "Um encurtador de URLs leve e autoalojado, com armazenamento SQLite.",
"A lightweight self-hosted music server with Subsonic API support.": "Um servidor de música leve e autoalojado, compatível com a API do Subsonic.",
"A lightweight self-hosted pastebin for text and files.": "Um serviço leve e autoalojado para partilhar texto e ficheiros.",
"A lightweight static homepage for organizing self-hosted services.": "Uma página inicial estática e leve para organizar serviços autoalojados.",
@@ -124,6 +126,7 @@
"A pending operation exists for": "Existe uma operation pendente para",
"A pending stack assembly already exists; it is not overwritten": "Já existe um conjunto de pilha pendente; não é substituído",
"A personal homepage for your apps and dashboards.": "Uma página inicial pessoal para as suas aplicações e painéis.",
+ "A personal, self-hosted microblog and timeline.": "Um microblog e uma cronologia pessoais, autoalojados.",
"A previous NVIDIA refresh is pending review": "Uma atualização anterior do NVIDIA está pendente revisão",
"A previous VFIO passthrough configuration was detected for the following NVIDIA GPU(s):": "Uma configuração de passagem VFIO anterior foi detectada para as seguintes GPUs NVIDIA:",
"A privacy-first, open-source platform for knowledge management and collaboration.": "Uma plataforma de privacidade-primeiro, open-source para gestão de conhecimento e colaboração.",
@@ -147,15 +150,18 @@
"A self-hosted Q&A and knowledge-sharing platform.": "Uma plataforma autoalojada de perguntas e respostas (Q&A) para partilha de conhecimento.",
"A self-hosted RSS reader inspired by Google Reader.": "Leitor RSS self-hosted inspirado no Google Reader.",
"A self-hosted app for organizing recurring tasks and household chores, with data stored in SQLite.": "Aplicação autoalojada para organizar tarefas e afazeres recorrentes, com armazenamento SQLite.",
+ "A self-hosted app for organizing tabletop role-playing campaigns.": "Uma aplicação autoalojada para organizar campanhas de jogos de interpretação de mesa.",
"A self-hosted bookmark manager and web archiver.": "Um gestor de marcadores com arquivo de páginas web, autoalojado.",
"A self-hosted bookmark manager.": "Gerenciador de favoritos self-hosted.",
"A self-hosted file sharing service with expiring downloads.": "Serviço self-hosted de compartilhamento de arquivos com downloads que expiram.",
"A self-hosted link-in-bio page.": "Página self-hosted de links para sua biografia.",
"A self-hosted read-it-later application.": "Uma aplicação autoalojada para guardar artigos e lê-los mais tarde.",
"A self-hosted task manager for planning work and tracking time.": "Um gestor de tarefas autoalojado para planear o trabalho e registar o tempo.",
+ "A self-hosted tracker for habits, tasks, notes and goals.": "Um gestor autoalojado de hábitos, tarefas, notas e objetivos.",
"A self-hosted vehicle maintenance and fuel log.": "Um registo autoalojado de manutenção de veículos e consumo de combustível.",
"A self-hosted workflow scheduler with a web interface.": "Agendador self-hosted de fluxos de trabalho com interface web.",
"A self-hosted, goal-free habit tracking tool.": "Uma ferramenta de rastreamento de hábitos auto-alojada e livre de objetivos.",
+ "A self-hosted, no-code database and application platform.": "Plataforma autoalojada para criar bases de dados e aplicações sem escrever código.",
"A self-improving AI agent with memory, skills, messaging, and a web dashboard.": "Um agente de IA auto-melhorando com memória, habilidades, mensagens e um painel web.",
"A server reboot is recommended for all changes to take full effect.": "Uma reinicialização do servidor é recomendada para que todas as alterações tenham efeito total.",
"A shared directory was replaced during the installation": "Um diretório compartilhado foi substituído durante a instalação",
@@ -276,6 +282,7 @@
"Active ZRAM algorithm, capacity or swap sources did not match the profile": "O algoritmo, a capacidade ou as origens de swap do ZRAM ativo não correspondem ao perfil",
"Active exports:": "Exportações ativas:",
"Active session:": "Sessão ativa:",
+ "Activepieces": "Activepieces",
"Actual device path on the host": "Localização real do dispositivo na máquina",
"Adaptation file too large": "Arquivo de adaptação muito grande",
"Adaptation file with unexpected permissions or owner": "Arquivo de adaptação com permissões inesperadas ou proprietário",
@@ -783,6 +790,8 @@
"Build and install the gasket and apex kernel modules (DKMS)": "Compilar e instalar os módulos de kernel gasket e apex (DKMS)",
"Build dependencies installed.": "Construa dependências instaladas.",
"Build dynamic websites using SQL files.": "Crie sites dinâmicos usando arquivos SQL.",
+ "Build reports and dashboards from your data.": "Crie relatórios e painéis com os seus dados.",
+ "Build visual automations for personal and hobby use.": "Crie automações visuais para uso pessoal e passatempos.",
"Build your personal knowledge base with TriliumNext Notes": "Crie sua base de conhecimento pessoal com TriliumPróximos Notas",
"Bulk generation requires GITHUB_TOKEN to obtain an immutable revision per application": "A geração em massa requer GITHUB_TOKEN para obter uma revisão imutável por aplicação",
"Business & ERP": "Negócio e ERP",
@@ -827,6 +836,7 @@
"Calibre-web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. It is also possible to integrate google drive and edit metadata and your calibre library through the app itself.": "Calibre-web é um aplicativo web que oferece uma interface limpa para navegação, leitura e download de eBooks usando um banco de dados existente de Calibre. Também é possível integrar o Google Drive e editar metadados e sua biblioteca calibre através do próprio aplicativo.",
"Calligra is an office and graphic art suite by KDE. It is available for desktop PCs, tablet computers, and smartphones. It contains applications for word processing, spreadsheets, presentation, vector graphics, and editing databases.": "Calligra é uma suite de arte gráfica e de escritório do KDE. Está disponível para computadores desktop, tablets e smartphones. Ele contém aplicativos para processamento de texto, planilhas, apresentação, gráficos vetoriais e bancos de dados de edição.",
"Calnode": "Calnode",
+ "Campfire first-run setup": "Configuração inicial do Campfire",
"Cancel": "Cancelar",
"Cancel restore": "Cancelar restauração",
"Cancel this setup": "cancelar esta configuração",
@@ -974,6 +984,8 @@
"Checklist post-upgrade finished. Warnings:": "Lista de verificação pós-atualização concluída. Avisos:",
"Checklist pre-check finished. Warnings:": "Pré-verificação da lista de verificação concluída. Avisos:",
"Checks for LVM and storage issues": "Verifica problemas de LVM e armazenamento",
+ "Chhoto URL": "Chhoto URL",
+ "Chhoto URL administrator password": "Palavra-passe de administrador do Chhoto URL",
"Choose": "Escolha",
"Choose BIOS Type": "Escolha o tipo de BIOS",
"Choose BIOS type": "Escolha o tipo de BIOS",
@@ -1192,6 +1204,7 @@
"Configured during installation": "Configurado durante a instalação.",
"Configured import disk %s as %s": "Disco de importação %s configurado como %s",
"Configured loader disk as ide0": "Disco do carregador configurado como ide0",
+ "Configured username": "Nome de utilizador configurado",
"Configured virtual disk as %s, %sGB on %s": "Disco virtual configurado como %s, %sGB em %s",
"Configuring AMD GPU passthrough...": "Configurando passagem de GPU AMD...",
"Configuring AMD softdep...": "Configurando o softdep AMD...",
@@ -1601,6 +1614,7 @@
"Create and stream custom live TV channels from your media library.": "Crie e transmita canais de TV ao vivo a partir da sua biblioteca multimédia.",
"Create credentials file (recommended):": "Crie um arquivo de credenciais (recomendado):",
"Create directory": "Criar diretório",
+ "Create encrypted, deduplicated backups and restore files.": "Crie cópias de segurança cifradas e deduplicadas e restaure ficheiros.",
"Create export directory:": "Crie o diretório de exportação:",
"Create mount point:": "Criar ponto de montagem:",
"Create new directory in /mnt": "Crie um novo diretório em /mnt",
@@ -1611,9 +1625,11 @@
"Create share directory:": "Crie um diretório de compartilhamento:",
"Create shared directory:": "Crie um diretório compartilhado:",
"Create this LXC in privileged mode?": "Criar este LXC em modo privilegiado?",
+ "Create with create_user in the container console": "Crie o utilizador com create_user na consola do contentor",
"Create, send and sign documents with a self-hosted web app.": "Crie, envie e assine documentos com uma aplicação web autoalojada.",
"Created common remapped user": "Usuário remapeado comum criado",
"Created directory on host:": "Diretório criado no host:",
+ "Created during first-run setup": "Criado durante a configuração inicial",
"Created persistent names for": "Nomes persistentes criados para",
"Created remapped user": "Usuário remapeado criado",
"Created sharedfiles group (GID: 101000)": "Grupo de arquivos compartilhados criado (GID: 101000)",
@@ -2127,6 +2143,7 @@
"Each LUN will appear as a block device assignable to VMs.": "Cada LUN aparecerá como um dispositivo de bloco atribuível às VMs.",
"Each peer gets its configuration and its QR code inside the container: /config/peer1/peer1.conf and /config/peer1/peer1.png, or /config/peer_/peer_.conf when names were given.": "Cada peer obtém sua configuração e seu código QR dentro do recipiente: /config/peer1/peer1.conf e /config/peer1/peer1.png, ou /config/peer /peer .conf quando os nomes foram dados.",
"Ebook and audiobook collection manager for Usenet and BitTorrent users.": "Gerenciador de coleção de ebooks e audiolivros para usuários Usenet e BitTorrent.",
+ "Ech0": "Ech0",
"Eden is an experimental open-source emulator for the Nintendo Switch, built with performance and stability in mind.": "Eden é um emulador experimental de código aberto para o Nintendo Switch, construído com desempenho e estabilidade em mente.",
"Edge TPU runtime installed.": "Tempo de execução do Edge TPU instalado.",
"Edit": "Editar",
@@ -2136,6 +2153,7 @@
"Edit the VM machine type to q35 and try again.": "Edite o tipo de máquina VM para q35 e tente novamente.",
"Edition:": "Edição:",
"Email address for certificate expiry notices (required by ZeroSSL)": "Endereço de e-mail para os avisos de validade do certificado (required by ZeroSSL)",
+ "Email address for the OpenHabitTracker account": "Endereço de e-mail da conta OpenHabitTracker",
"Email address of the LibreDB Studio administrator": "Endereço de e-mail do administrador LibreDB Studio",
"Email address of the NetBox admin account": "Endereço de e-mail da conta de administrador NetBox",
"Email address of the admin account": "Endereço de e-mail da conta de administrador",
@@ -2299,6 +2317,7 @@
"Enter the BORG REPOKEY passphrase for target:": "Digite a senha do BORG REPOKEY para o destino:",
"Enter the Samba username:": "Digite o nome de usuário do Samba:",
"Enter the VMID for the new VM:": "Insira o VMID da nova VM:",
+ "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Introduza o URL base que os clientes e fornecedores de webhooks utilizarão para aceder a esta instância do Activepieces.",
"Enter the container terminal:": "Entre no terminal de contêineres:",
"Enter the full path to the directory containing disk images:": "Digite o caminho completo para o diretório que contém as imagens de disco:",
"Enter the full path to the host directory:": "Digite o caminho completo para o diretório host:",
@@ -2668,7 +2687,9 @@
"Firewall allows port": "Firewall permite porta",
"Firewall settings": "Configurações de firewall",
"Firmware :": "Firmware:",
+ "First Ech0 account (becomes Owner)": "Primeira conta Ech0 (passa a ser proprietária)",
"First Jelu administrator": "Primeiro administrador do Jelu",
+ "First Watcharr account (becomes administrator)": "Primeira conta Watcharr (passa a ser administradora)",
"First complete DSM setup and verify Web UI/SSH access.": "Primeiro, conclua a configuração do DSM e verifique o acesso Web UI/SSH.",
"First complete ZimaOS setup and verify remote access (web/SSH).": "Primeiro conclua a configuração do ZimaOS e verifique o acesso remoto (web/SSH).",
"First install the GPU drivers inside the guest and verify remote access (RDP/SSH).": "Primeiro instale os drivers GPU dentro do convidado e verifique o acesso remoto (RDP/SSH).",
@@ -3306,6 +3327,7 @@
"Initial Redmine administrator": "Administrador inicial do Redmine",
"Initial Stirling-PDF account": "Conta inicial do Stirling-PDF",
"Initial Tandoor administrator": "Administrador inicial Tandoor",
+ "Initial Umami administrator": "Administrador inicial do Umami",
"Initial administrator created:": "Administrador inicial criado:",
"Initial administrator user": "Utilizador de administrador inicial",
"Initializing Borg repository if needed...": "Inicializando o repositório Borg, se necessário...",
@@ -3404,6 +3426,7 @@
"Installing Fail2Ban...": "Instalando Fail2Ban...",
"Installing Git as a prerequisite...": "Instalando o Git como pré-requisito...",
"Installing Intel VA-API drivers in container...": "Instalando drivers Intel VA-API no contêiner...",
+ "Installing Log2RAM": "Instalando Log2RAM",
"Installing Log2RAM from GitHub...": "Instalando Log2RAM do GitHub...",
"Installing Log2RAM from source...": "Instalando Log2RAM da fonte...",
"Installing NFS Client in LXC": "Instalando o cliente NFS no LXC",
@@ -3753,6 +3776,8 @@
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa é uma ferramenta poderosa projetada para lhe dar controle completo sobre suas bibliotecas de mídia. Com o Kometa, você pode levar sua personalização para o próximo nível, com controle granular sobre metadados, coleções, sobreposições e muito mais.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa lê a sua configuração a partir de /config/config.yml e o recipiente apenas navios /config/config.yml.template. Copie o modelo para config.yml, preencha o requi vermelho Plex e conexões TMDb, em seguida, reinicie o recipiente.",
"Komga is a media server for your comics, mangas, BDs, magazines and eBooks.": "Komga é um servidor de mídia para seus quadrinhos, mangás, BDs, revistas e eBooks.",
+ "Kopia Web UI": "Interface web do Kopia",
+ "Kopia Web UI login": "Acesso à interface web do Kopia",
"Krita is a professional FREE and open source painting program. It is made by artists that want to see affordable art tools for everyone.": "Krita é um programa de pintura livre e livre profissional. É feito por artistas que querem ver ferramentas de arte acessíveis para todos.",
"LAN access to the kept containers (stack configuration incomplete):": "Acesso LAN aos recipientes mantidos (configuração incompleta):",
"LAN address applied to the application URLs": "Endereço LAN aplicado aos URLs da aplicação",
@@ -3894,10 +3919,14 @@
"Log aggregation server for Grafana.": "Servidor de agregação de logs para o Grafana.",
"Log file": "Arquivo de registro",
"Log2RAM already registered — updating to latest configuration": "Log2RAM já registrado — atualizando para a configuração mais recente",
+ "Log2RAM could not be downloaded": "Não foi possível baixar o Log2RAM",
"Log2RAM installation and configuration completed successfully.": "Instalação e configuração do Log2RAM concluídas com sucesso.",
"Log2RAM installation cancelled by user": "Instalação do Log2RAM cancelada pelo usuário",
"Log2RAM installation verification failed. Check /tmp/log2ram_install.log": "A verificação da instalação do Log2RAM falhou. Verifique /tmp/log2ram_install.log",
"Log2RAM installed successfully": "Log2RAM instalado com sucesso",
+ "Log2RAM installed:": "Log2RAM instalado:",
+ "Log2RAM is removed at the next reboot, after writing /var/log to disk": "O Log2RAM é removido na próxima reinicialização, depois de gravar /var/log no disco",
+ "Log2RAM keeps /var/log in RAM; the journal stays on disk from the next boot": "O Log2RAM mantém /var/log na RAM; o journal fica no disco a partir da próxima inicialização",
"Log2RAM keeps the journal in order from the next reboot": "Log2RAM mantém o diário em ordem a partir da próxima reinicialização",
"Log2RAM size set to:": "Tamanho do Log2RAM definido como:",
"Log2RAM write scheduled every": "Gravação Log2RAM agendada a cada",
@@ -3980,6 +4009,7 @@
"Manage custom backup paths": "Gerencie caminhos de backup personalizados",
"Manage custom paths (add / remove your folders)": "Gerencie caminhos personalizados (adicione/remova suas pastas)",
"Manage data and APIs with a modern visual interface.": "Faça a gestão de dados e APIs através de uma interface visual moderna.",
+ "Manage database changes, access and workflows.": "Gira alterações à base de dados, acessos e fluxos de trabalho.",
"Manage installed OCI applications": "Gerenciar aplicativos OCI instalados",
"Manage local backup target": "Gerenciar destino de backup local",
"Manage private image registries.\nSaved credentials let ProxMenux access your private images.": "Gerencie registros de imagens privadas.\nAs credenciais salvas permitem que ProxMenux acesse suas imagens privadas.",
@@ -4847,6 +4877,9 @@
"Open-source in-memory data store.": "Armazenamento de dados em memória de código aberto.",
"Open-source self-hosted SQL IDE.": "SQL IDE self-hosted do código aberto.",
"OpenClaw is a personal AI assistant you run on your own devices": "OpenClaw é um assistente pessoal de IA que você executa em seus próprios dispositivos",
+ "OpenHabitTracker": "OpenHabitTracker",
+ "OpenHabitTracker account": "Conta OpenHabitTracker",
+ "OpenHabitTracker initial username": "Nome de utilizador inicial do OpenHabitTracker",
"OpenList": "OpenList",
"OpenObserve administrator": "Administrador do OpenObserve",
"OpenShot Video Editor is an award-winning free and open-source video editor for Linux, Mac, and Windows, and is dedicated to delivering high quality video editing and animation solutions to the world.": "O OpenShot Video Editor é um premiado editor de vídeo gratuito e de código aberto para Linux, Mac e Windows, e é dedicado a fornecer soluções de edição e animação de vídeo de alta qualidade para o mundo.",
@@ -5214,6 +5247,7 @@
"Primary upstream DNS server": "Servidor DNS a montante principal",
"Privacy-first finance app with envelope budgeting and multi-device sync.": "App Privacy-first finance com envelope budgeting e sincronização multidispositivo.",
"Privacy-first, self-hosted PDF toolkit": "Privacy-primeiro, auto-hosted PDF toolkit",
+ "Privacy-focused, self-hosted web analytics.": "Análise web auto-hospedada, com foco na privacidade.",
"Private installation record saved": "Gravação de instalação privada salva",
"Private network assigned automatically:": "Rede privada atribuída automaticamente:",
"Private network kept, because other containers still use it:": "Mensagem técnica para Proxmox e TI.Traduzindo: Rede privada mantida, pois outros containers ainda a utilizam:",
@@ -5337,6 +5371,7 @@
"Public UDP port clients connect to": "Clientes públicos de porta UDP se conectam",
"Public UDP port peers connect to": "Os pares públicos de portas UDP se conectam",
"Public URL of phpMyAdmin when it is served behind a reverse proxy": "URL público do phpMyAdmin quando é servido por trás de um proxy reverso",
+ "Public URL used to access Baserow (for example https://baserow.example.com)": "URL público usado para aceder ao Baserow (por exemplo https://baserow.example.com)",
"Public address clients connect to (vpn.example.com or a public IP)": "Clientes de endereço público (vpn.example.com ou IP público)",
"Public address peers connect to, or auto to detect it (vpn.example.com)": "Os pares de endereços públicos se conectam ou se autopara detectá-lo (vpn.example.com)",
"Publish each directory from Resources: open it, switch on SMB, NFS or both, and save.": "Publique cada diretório de Recursos: abra-o, ative SMB, NFS ou ambos e salve.",
@@ -5362,6 +5397,7 @@
"RAM in MiB": "RAM em MiB",
"REPAIR SUMMARY": "RESUMO DO REPARO",
"REQUIREMENTS:": "REQUISITOS:",
+ "REST API Basic Authentication user": "Utilizador da autenticação básica da API REST",
"ROCm does not support this GPU officially. The application can use it by presenting it as another generation of its family: it is faster than the CPU, but under load the GPU can stop responding, and then the application does not answer until its container is restarted. The ROCm image is also larger than the others.": "ROCm não suporta oficialmente esta GPU. A aplicação pode utilizá-la apresentando-a como outra geração de sua família: é mais rápida que a CPU, mas sob carga a GPU pode parar de responder, e então a aplicação não responde até que seu contêiner seja reiniciado. Além disso, a imagem ROCm é maior que as demais.",
"ROCm requires /dev/kfd on the host": "ROCm requer /dev/kfd no host",
"ROCm requires the render device of an AMD GPU": "ROCm requer o dispositivo de renderização de um AMD GPU",
@@ -5672,6 +5708,7 @@
"Repositories switched to no-subscription": "Repositórios alterados para sem assinatura",
"Repository check failed. NVIDIA installation stopped.": "Falha na verificação do repositório. A instalação do NVIDIA foi interrompida.",
"Repository check failed. Update stopped.": "Falha na verificação do repositório. Atualização interrompida.",
+ "Repository encryption password": "Palavra-passe de encriptação do repositório",
"Repository helper unavailable. NVIDIA installation stopped.": "Auxiliar de repositório indisponível. A instalação do NVIDIA foi interrompida.",
"Repository policy returned an unexpected result.": "A política do repositório retornou um resultado inesperado.",
"Repository ready.": "Repositório pronto.",
@@ -5789,6 +5826,7 @@
"Root shell/profile config": "Configuração de shell/perfil raiz",
"Root user on the PVE host (default 'root'):": "Usuário root no host PVE (padrão 'root'):",
"Rootfs size in GB": "Tamanho do Rootfs em GB",
+ "Rotate and write /var/log to disk when it passes 85% of its size?": "Rotacionar e gravar /var/log no disco quando ultrapassar 85% do seu tamanho?",
"Rotate the recovery passphrase": "gire a senha de recuperação",
"Routing Information": "Informações de roteamento",
"Routing Table": "Tabela de roteamento",
@@ -6261,6 +6299,7 @@
"Set ownership and permissions:": "Defina propriedade e permissões:",
"Set the initial Technitium DNS administrator password": "Defina a senha inicial do administrador do Technitium DNS.",
"Set this disk as the primary boot disk?": "Definir este disco como disco de inicialização principal?",
+ "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Defina como 1 apenas numa LAN privada que utilize exclusivamente HTTP; caso contrário, utilize HTTPS.",
"Set up custom MOTD banner": "configurar banner MOTD personalizado",
"Set up the apex group and udev rules": "Configure o grupo apex e as regras do udev",
"Set up time synchronization and entropy generation": "Configurar sincronização de tempo e geração de entropia",
@@ -6966,6 +7005,7 @@
"The domain must resolve to the public address of this network before the certificate can be issued.": "O domínio deve ser resolvido para o endereço público desta rede antes que o certificado possa ser emitido.",
"The download client still needs to be configured.": "O cliente de download ainda precisa ser configurado.",
"The download stopped progressing; cancelling this attempt.": "O download parou de progredir; cancelando esta tentativa.",
+ "The downloaded Log2RAM does not match the expected release": "O Log2RAM baixado não corresponde à versão esperada",
"The downloaded image does not match its manifest": "A imagem transferida não corresponde ao seu manifesto",
"The downloaded image is corrupt:": "A imagem baixada está corrompida:",
"The dpkg package database is clean.": "O banco de dados do pacote dpkg está limpo.",
@@ -7115,6 +7155,7 @@
"The oci-compose is incomplete: it does not say which image to install.": "O oci-compose está incompleto: não diz qual imagem instalar.",
"The oci-compose is not valid JSON:": "O oci-compose não é JSON válido:",
"The official Tandoor startup executable is missing": "Falta o executável oficial de inicialização Tandoor",
+ "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Os serviços Compose oficiais do Umami e do PostgreSQL não exigem modo privilegiado nem que o confinamento do host seja flexibilizado.",
"The official inventory contains no NVIDIA devices": "O inventário oficial não contém dispositivos NVIDIA",
"The official inventory contains no NVIDIA driver components": "O inventário oficial não contém componentes do driver NVIDIA",
"The official startup cannot be reproduced": "A inicialização oficial não pode ser reproduzida",
@@ -7535,6 +7576,7 @@
"Total routes": "Rotas totais",
"Total size:": "Tamanho total:",
"Toxiproxy": "Toxiproxy",
+ "Track movies, television, anime and games you want to enjoy.": "Acompanhe os filmes, séries, anime e videojogos que quer ver ou jogar.",
"Track subscriptions and recurring expenses.": "Acompanhe subscrições e despesas recorrentes.",
"Track your music listening history on your own server.": "Acompanhe seu histórico de músicas ouvidas no seu próprio servidor.",
"Track your reading, books and history in a private library.": "Acompanhe suas leituras, livros e histórico em uma biblioteca privada.",
@@ -7614,6 +7656,7 @@
"Uninstalling Ceph...": "Desinstalando o Ceph...",
"Uninstalling Fastfetch...": "Desinstalando o Fastfetch...",
"Uninstalling Figurine...": "Desinstalando a estatueta...",
+ "Uninstalling Log2RAM...": "Desinstalando Log2RAM...",
"Uninstalling amdgpu_top...": "Desinstalando amdgpu_top...",
"Uninstalling current NVIDIA driver before installing new version": "Desinstalando o driver NVIDIA atual antes de instalar a nova versão",
"Uninstalling intel-gpu-tools...": "Desinstalando ferramentas Intel-GPU...",
@@ -8092,6 +8135,7 @@
"Warning: both VMs have autostart enabled (onboot=1).": "Aviso: ambas as VMs têm inicialização automática habilitada (onboot=1).",
"Warnings": "Avisos",
"Watch history, ratings and watchlists synced between media servers and trackers.": "Histórico, classificações e listas sincronizados entre servidores multimédia e trackers.",
+ "Watcharr": "Watcharr",
"Watchdog": "Vigilância",
"Watchdog (off): restart the application when it crashes": "Vigilância (desativada): reiniciar a aplicação quando falha",
"Watchdog (on): restart the application when it crashes": "Vigilância (ativada): reiniciar a aplicação quando falha",
@@ -8869,10 +8913,5 @@
"⚠ Disk data will NOT be erased.": "⚠ Os dados do disco NÃO serão apagados.",
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ O disco será desmontado e removido de /etc/fstab.",
"⚠ The /etc/fstab entry will be removed.": "⚠ A entrada /etc/fstab será removida.",
- "⚠ The disk will be unmounted.": "⚠ O disco será desmontado.",
- "Initial Umami administrator": "Administrador inicial do Umami",
- "Privacy-focused, self-hosted web analytics.": "Análise web auto-hospedada, com foco na privacidade.",
- "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Os serviços Compose oficiais do Umami e do PostgreSQL não exigem modo privilegiado nem que o confinamento do host seja flexibilizado.",
- "A self-hosted, no-code database and application platform.": "Plataforma autoalojada para criar bases de dados e aplicações sem escrever código.",
- "Public URL used to access Baserow (for example https://baserow.example.com)": "URL público usado para aceder ao Baserow (por exemplo https://baserow.example.com)"
+ "⚠ The disk will be unmounted.": "⚠ O disco será desmontado."
}
diff --git a/lang/sk.json b/lang/sk.json
index 28c1527d..83546d74 100644
--- a/lang/sk.json
+++ b/lang/sk.json
@@ -47,6 +47,7 @@
"A GPS tracking platform with device management and fleet reports.": "Platforma na sledovanie GPS so správou zariadení a prehľadmi o vozovom parku.",
"A GTK Broadway web UI for libvirt and virt-manager.": "GTK Broadway web UI pre libvirt a virt-manager.",
"A Personal Relationship Management tool to help you document your social life.": "Nástroj na správu osobných vzťahov a zaznamenávanie spoločenského života.",
+ "A REST backend for restic backups; it has no web interface.": "REST backend na zálohy restic bez webového rozhrania.",
"A VirtIO ISO already exists. Do you want to overwrite it?": "VirtIO ISO už existuje. Chcete ho prepísať?",
"A ZFS pool with this name already exists.": "ZFS pool s týmto názvom už existuje.",
"A ZFS pool with this name already exists:": "ZFS pool s týmto názvom už existuje:",
@@ -94,6 +95,7 @@
"A lightweight dashboard that brings feeds, services and useful widgets together.": "Nenáročný informačný panel, ktorý spája kanály, služby a užitočné widgety.",
"A lightweight hub for monitoring servers and containers.": "Nenáročné centrum na monitorovanie serverov a kontajnerov.",
"A lightweight self-hosted Kanban board.": "Nenáročná Kanban tabuľa prevádzkovaná vo vlastnej réžii.",
+ "A lightweight self-hosted URL shortener with SQLite storage.": "Odľahčovač URL adries s vlastným hostingom a úložiskom SQLite.",
"A lightweight self-hosted music server with Subsonic API support.": "Nenáročný hudobný server prevádzkovaný vo vlastnej réžii s podporou Subsonic API.",
"A lightweight self-hosted pastebin for text and files.": "Nenáročná služba na zdieľanie textu a súborov prevádzkovaná vo vlastnej réžii.",
"A lightweight static homepage for organizing self-hosted services.": "Nenáročná statická domovská stránka na organizovanie vlastných služieb.",
@@ -124,6 +126,7 @@
"A pending operation exists for": "Nedokončená operácia existuje pre",
"A pending stack assembly already exists; it is not overwritten": "Už existuje nedokončené zostavenie aplikácie; neprepíše sa",
"A personal homepage for your apps and dashboards.": "Osobná domovská stránka pre vaše aplikácie a informačné panely.",
+ "A personal, self-hosted microblog and timeline.": "Osobný mikroblog a časová os s vlastným hostingom.",
"A previous NVIDIA refresh is pending review": "Predchádzajúce obnovovanie NVIDIA čaká na preskúmanie",
"A previous VFIO passthrough configuration was detected for the following NVIDIA GPU(s):": "Pre nasledujúce GPU NVIDIA bola zistená predchádzajúca konfigurácia priechodu VFIO:",
"A privacy-first, open-source platform for knowledge management and collaboration.": "Platforma na správu znalostí a spoluprácu s otvoreným zdrojovým kódom a dôrazom na súkromie.",
@@ -147,15 +150,18 @@
"A self-hosted Q&A and knowledge-sharing platform.": "Platforma na otázky a odpovede a zdieľanie znalostí prevádzkovaná vo vlastnej réžii.",
"A self-hosted RSS reader inspired by Google Reader.": "Vlastný RSS čítač inšpirovaný službou Google Reader.",
"A self-hosted app for organizing recurring tasks and household chores, with data stored in SQLite.": "Samohostovaná aplikácia na organizáciu opakujúcich sa úloh a domácich prác s ukladaním do SQLite.",
+ "A self-hosted app for organizing tabletop role-playing campaigns.": "Aplikácia s vlastným hostingom na organizovanie kampaní stolových hier na hrdinov.",
"A self-hosted bookmark manager and web archiver.": "Správca záložiek a webový archivátor prevádzkovaný vo vlastnej réžii.",
"A self-hosted bookmark manager.": "Vlastný správca záložiek.",
"A self-hosted file sharing service with expiring downloads.": "Vlastná služba na zdieľanie súborov so sťahovaním s obmedzenou platnosťou.",
"A self-hosted link-in-bio page.": "Vlastná stránka s odkazmi v profile.",
"A self-hosted read-it-later application.": "Aplikácia na ukladanie článkov na neskoršie čítanie prevádzkovaná vo vlastnej réžii.",
"A self-hosted task manager for planning work and tracking time.": "Správca úloh na plánovanie práce a sledovanie času prevádzkovaný vo vlastnej réžii.",
+ "A self-hosted tracker for habits, tasks, notes and goals.": "Sledovanie návykov, úloh, poznámok a cieľov s vlastným hostingom.",
"A self-hosted vehicle maintenance and fuel log.": "Záznamník údržby vozidiel a spotreby paliva prevádzkovaný vo vlastnej réžii.",
"A self-hosted workflow scheduler with a web interface.": "Vlastný plánovač pracovných postupov s webovým rozhraním.",
"A self-hosted, goal-free habit tracking tool.": "Nástroj na sledovanie návykov bez stanovovania cieľov, prevádzkovaný na vlastnom serveri.",
+ "A self-hosted, no-code database and application platform.": "Databázová a aplikačná platforma bez kódu, ktorú si hostujete sami.",
"A self-improving AI agent with memory, skills, messaging, and a web dashboard.": "Zdokonaľujúci sa agent AI s pamäťou, zručnosťami, komunikáciou a webovým panelom.",
"A server reboot is recommended for all changes to take full effect.": "Odporúča sa reštart servera, aby sa všetky zmeny naplno prejavili.",
"A shared directory was replaced during the installation": "Počas inštalácie bol vymenený zdieľaný adresár",
@@ -276,6 +282,7 @@
"Active ZRAM algorithm, capacity or swap sources did not match the profile": "Algoritmus kompresie, veľkosť alebo zdroje swapu aktívnej ZRAM nezodpovedajú profilu",
"Active exports:": "Aktívne exporty:",
"Active session:": "Aktívna relácia:",
+ "Activepieces": "Activepieces",
"Actual device path on the host": "Skutočná cesta k zariadeniu na hostiteľovi",
"Adaptation file too large": "Adaptačný súbor príliš veľký",
"Adaptation file with unexpected permissions or owner": "Adaptačný súbor s neočakávanými oprávneniami alebo majiteľom",
@@ -783,6 +790,8 @@
"Build and install the gasket and apex kernel modules (DKMS)": "Zostaví a nainštaluje kernel moduly gasket a apex (DKMS)",
"Build dependencies installed.": "Závislosti na zostavenie boli nainštalované.",
"Build dynamic websites using SQL files.": "Vytvárajte dynamické webové stránky pomocou súborov SQL.",
+ "Build reports and dashboards from your data.": "Vytvárajte prehľady a dashboardy zo svojich údajov.",
+ "Build visual automations for personal and hobby use.": "Vytvárajte vizuálne automatizácie na osobné použitie a koníčky.",
"Build your personal knowledge base with TriliumNext Notes": "Vytvorte si osobnú znalostnú databázu s TriliumNext Notes",
"Bulk generation requires GITHUB_TOKEN to obtain an immutable revision per application": "Hromadné generovanie vyžaduje GITHUB_TOKEN na získanie nemennej revízie každej aplikácie",
"Business & ERP": "Biznis a ERP",
@@ -827,6 +836,7 @@
"Calibre-web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. It is also possible to integrate google drive and edit metadata and your calibre library through the app itself.": "Calibre-web je webová aplikácia poskytujúca čisté rozhranie pre prehliadanie, čítanie a sťahovanie elektronických kníh pomocou existujúcej databázy Calibre. Je tiež možné integrovať Google disk a upraviť metaúdaje a knižnicu calibre prostredníctvom samotnej aplikácie.",
"Calligra is an office and graphic art suite by KDE. It is available for desktop PCs, tablet computers, and smartphones. It contains applications for word processing, spreadsheets, presentation, vector graphics, and editing databases.": "Calligra je kancelársky a grafický balík od KDE pre počítače, tablety a smartfóny. Obsahuje aplikácie na spracovanie textu, tabuľky, prezentácie, vektorovú grafiku a úpravu databáz.",
"Calnode": "Calnode",
+ "Campfire first-run setup": "Prvotné nastavenie Campfire",
"Cancel": "Zrušiť",
"Cancel restore": "Zrušiť obnovu",
"Cancel this setup": "Zrušiť toto nastavenie",
@@ -974,6 +984,8 @@
"Checklist post-upgrade finished. Warnings:": "Kontrola po aktualizácii dokončená. Varovania:",
"Checklist pre-check finished. Warnings:": "Predbežná kontrola dokončená. Varovania:",
"Checks for LVM and storage issues": "Skontroluje problémy s LVM a úložiskami",
+ "Chhoto URL": "Chhoto URL",
+ "Chhoto URL administrator password": "Heslo správcu pre Chhoto URL",
"Choose": "Vyber",
"Choose BIOS Type": "Vyber typ BIOS",
"Choose BIOS type": "Vyberte typ BIOSu",
@@ -1192,6 +1204,7 @@
"Configured during installation": "Nastaví sa počas inštalácie",
"Configured import disk %s as %s": "Importovaný disk %s je nastavený ako %s",
"Configured loader disk as ide0": "Disk zavádzača je nastavený ako ide0",
+ "Configured username": "Nastavené používateľské meno",
"Configured virtual disk as %s, %sGB on %s": "Virtuálny disk je nastavený ako %s, %sGB na %s",
"Configuring AMD GPU passthrough...": "Nastavujem AMD GPU passthrough...",
"Configuring AMD softdep...": "Nastavujem AMD softdep...",
@@ -1601,6 +1614,7 @@
"Create and stream custom live TV channels from your media library.": "Vytvárajte vlastné živé TV kanály a streamujte ich zo svojej knižnice médií.",
"Create credentials file (recommended):": "Vytvoriť súbor s prihlasovacími údajmi (odporúčané):",
"Create directory": "Vytvoriť priečinok",
+ "Create encrypted, deduplicated backups and restore files.": "Vytvárajte šifrované, deduplikované zálohy a obnovujte súbory.",
"Create export directory:": "Vytvoriť exportovaný priečinok:",
"Create mount point:": "Vytvoriť mount point:",
"Create new directory in /mnt": "Vytvoriť nový priečinok v /mnt",
@@ -1611,9 +1625,11 @@
"Create share directory:": "Vytvoriť zdieľaný priečinok:",
"Create shared directory:": "Vytvoriť zdieľaný priečinok:",
"Create this LXC in privileged mode?": "Vytvoriť tento LXC v privilegovanom režime?",
+ "Create with create_user in the container console": "Používateľa vytvorte príkazom create_user v konzole kontajnera",
"Create, send and sign documents with a self-hosted web app.": "Vytvárajte, posielajte a podpisujte dokumenty vo webovej aplikácii prevádzkovanej vo vlastnej réžii.",
"Created common remapped user": "Vytvorený spoločný remapovaný používateľ",
"Created directory on host:": "Priečinok vytvorený na hoste:",
+ "Created during first-run setup": "Vytvorí sa počas prvotného nastavenia",
"Created persistent names for": "Vytvorené trvalé názvy pre",
"Created remapped user": "Vytvorený remapovaný používateľ",
"Created sharedfiles group (GID: 101000)": "Vytvorená skupina sharedfiles (GID: 101000)",
@@ -2127,6 +2143,7 @@
"Each LUN will appear as a block device assignable to VMs.": "Každý LUN sa zobrazí ako blokové zariadenie, ktoré sa dá priradiť k VM.",
"Each peer gets its configuration and its QR code inside the container: /config/peer1/peer1.conf and /config/peer1/peer1.png, or /config/peer_/peer_.conf when names were given.": "Každý účastník má v kontajneri konfiguráciu a QR kód: /config/peer1/peer1.conf a /config/peer1/peer1.png alebo /config/peer_/peer_.conf, ak boli zadané mená.",
"Ebook and audiobook collection manager for Usenet and BitTorrent users.": "Správca zbierky elektronických kníh a audiokníh pre používateľov Usenetu a BitTorrentu.",
+ "Ech0": "Ech0",
"Eden is an experimental open-source emulator for the Nintendo Switch, built with performance and stability in mind.": "Eden je experimentálny emulátor Nintendo Switch s otvoreným zdrojovým kódom a dôrazom na výkon a stabilitu.",
"Edge TPU runtime installed.": "Edge TPU runtime bol nainštalovaný.",
"Edit": "Upraviť",
@@ -2136,6 +2153,7 @@
"Edit the VM machine type to q35 and try again.": "Zmeňte typ stroja VM na q35 a skúste to znova.",
"Edition:": "Edícia:",
"Email address for certificate expiry notices (required by ZeroSSL)": "E-mail na upozornenia pred vypršaním certifikátu (vyžaduje ZeroSSL)",
+ "Email address for the OpenHabitTracker account": "E-mailová adresa účtu OpenHabitTracker",
"Email address of the LibreDB Studio administrator": "E-mailová adresa správcu LibreDB Studio",
"Email address of the NetBox admin account": "E-mailová adresa admin účtu NetBox",
"Email address of the admin account": "E-mailová adresa účtu správcu",
@@ -2299,6 +2317,7 @@
"Enter the BORG REPOKEY passphrase for target:": "Zadajte BORG REPOKEY frázu pre cieľ:",
"Enter the Samba username:": "Zadajte používateľské meno Samby:",
"Enter the VMID for the new VM:": "Zadajte VMID pre novú VM:",
+ "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Zadajte základnú adresu URL, ktorú klienti a poskytovatelia webhookov použijú na prístup k tejto inštancii Activepieces.",
"Enter the container terminal:": "Vstúpiť do terminálu kontajnera:",
"Enter the full path to the directory containing disk images:": "Zadajte úplnú cestu k priečinku s diskovými obrazmi:",
"Enter the full path to the host directory:": "Zadajte úplnú cestu k priečinku na hostovi:",
@@ -2668,7 +2687,9 @@
"Firewall allows port": "Firewall povoľuje port",
"Firewall settings": "Nastavenia firewallu",
"Firmware :": "Firmware :",
+ "First Ech0 account (becomes Owner)": "Prvý účet Ech0 (stane sa vlastníkom)",
"First Jelu administrator": "Prvý správca Jelu",
+ "First Watcharr account (becomes administrator)": "Prvý účet Watcharr (stane sa správcom)",
"First complete DSM setup and verify Web UI/SSH access.": "Najprv dokončite nastavenie DSM a overte prístup cez webové rozhranie alebo SSH.",
"First complete ZimaOS setup and verify remote access (web/SSH).": "Najprv dokončite nastavenie ZimaOS a overte vzdialený prístup (web/SSH).",
"First install the GPU drivers inside the guest and verify remote access (RDP/SSH).": "Najprv nainštalujte ovládače GPU vo vnútri VM a overte vzdialený prístup (RDP/SSH).",
@@ -3306,6 +3327,7 @@
"Initial Redmine administrator": "Počiatočný správca Redmine",
"Initial Stirling-PDF account": "Počiatočný účet Stirling-PDF",
"Initial Tandoor administrator": "Počiatočný správca Tandoor",
+ "Initial Umami administrator": "Počiatočný správca Umami",
"Initial administrator created:": "Pôvodný správca vytvorený:",
"Initial administrator user": "Počiatočné používateľské meno správcu",
"Initializing Borg repository if needed...": "Pripravujem Borg repozitár, ak je to potrebné...",
@@ -3404,6 +3426,7 @@
"Installing Fail2Ban...": "Inštalujem Fail2Ban...",
"Installing Git as a prerequisite...": "Inštalujem Git ako požiadavku...",
"Installing Intel VA-API drivers in container...": "Inštalujem Intel VA-API ovládače v kontajneri...",
+ "Installing Log2RAM": "Inštalujem Log2RAM",
"Installing Log2RAM from GitHub...": "Inštalujem Log2RAM z GitHubu...",
"Installing Log2RAM from source...": "Inštalujem Log2RAM zo zdrojového kódu...",
"Installing NFS Client in LXC": "Inštalujem NFS klienta v LXC",
@@ -3753,6 +3776,8 @@
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa poskytuje podrobnú kontrolu nad mediálnymi knižnicami. Umožňuje prispôsobiť metadáta, kolekcie, grafické prekrytia a ďalšie prvky.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa číta nastavenia z /config/config.yml, kontajner však obsahuje iba /config/config.yml.template. Skopírujte šablónu do config.yml, vyplňte požadované pripojenia Plex a TMDb a kontajner reštartujte.",
"Komga is a media server for your comics, mangas, BDs, magazines and eBooks.": "Komga je mediálny server na komiksy, mangu, grafické romány, časopisy a elektronické knihy.",
+ "Kopia Web UI": "Webové rozhranie Kopia",
+ "Kopia Web UI login": "Prihlásenie do webového rozhrania Kopia",
"Krita is a professional FREE and open source painting program. It is made by artists that want to see affordable art tools for everyone.": "Krita je profesionálny bezplatný maliarsky program s otvoreným zdrojovým kódom. Vytvárajú ho umelci, ktorí chcú sprístupniť umelecké nástroje každému.",
"LAN access to the kept containers (stack configuration incomplete):": "Prístup z LAN k ponechaným kontajnerom (nastavenie zostavy nie je dokončené):",
"LAN address applied to the application URLs": "LAN adresa použitá na URL aplikácie",
@@ -3894,10 +3919,14 @@
"Log aggregation server for Grafana.": "Server na zhromažďovanie logov pre Grafanu.",
"Log file": "Súbor záznamu",
"Log2RAM already registered — updating to latest configuration": "Log2RAM už je zaregistrovaný - aktualizujem ho na najnovšie nastavenie",
+ "Log2RAM could not be downloaded": "Log2RAM sa nepodarilo stiahnuť",
"Log2RAM installation and configuration completed successfully.": "Inštalácia a nastavenie Log2RAM boli úspešne dokončené.",
"Log2RAM installation cancelled by user": "Inštalácia Log2RAM bola zrušená používateľom",
"Log2RAM installation verification failed. Check /tmp/log2ram_install.log": "Overenie inštalácie Log2RAM zlyhalo. Skontrolujte /tmp/log2ram_install.log",
"Log2RAM installed successfully": "Log2RAM bol úspešne nainštalovaný",
+ "Log2RAM installed:": "Log2RAM nainštalovaný:",
+ "Log2RAM is removed at the next reboot, after writing /var/log to disk": "Log2RAM sa odstráni pri ďalšom reštarte, po zápise /var/log na disk",
+ "Log2RAM keeps /var/log in RAM; the journal stays on disk from the next boot": "Log2RAM drží /var/log v RAM; journal zostáva na disku od nasledujúceho spustenia",
"Log2RAM keeps the journal in order from the next reboot": "Log2RAM udržiava žurnál v poriadku od nasledujúceho reštartu",
"Log2RAM size set to:": "Veľkosť Log2RAM nastavená na:",
"Log2RAM write scheduled every": "Zápis z Log2RAM je naplánovaný každých",
@@ -3980,6 +4009,7 @@
"Manage custom backup paths": "Spravovať vlastné cesty zálohy",
"Manage custom paths (add / remove your folders)": "Spravovať vlastné cesty (pridať / odstrániť priečinky)",
"Manage data and APIs with a modern visual interface.": "Spravujte údaje a API pomocou moderného vizuálneho rozhrania.",
+ "Manage database changes, access and workflows.": "Spravujte zmeny v databáze, prístupy a pracovné postupy.",
"Manage installed OCI applications": "Spravovať nainštalované aplikácie OCI",
"Manage local backup target": "Spravovať lokálny cieľ záloh",
"Manage private image registries.\nSaved credentials let ProxMenux access your private images.": "Správa súkromných registrov obrazov.\nUložené prihlasovacie údaje umožňujú ProxMenuxu pristupovať k vašim súkromným obrazom.",
@@ -4849,6 +4879,9 @@
"Open-source in-memory data store.": "Úložisko údajov v pamäti s otvoreným zdrojovým kódom.",
"Open-source self-hosted SQL IDE.": "Vývojové prostredie SQL s otvoreným zdrojovým kódom na vlastnom serveri.",
"OpenClaw is a personal AI assistant you run on your own devices": "OpenClaw je osobný asistent AI, ktorý prevádzkujete na vlastných zariadeniach",
+ "OpenHabitTracker": "OpenHabitTracker",
+ "OpenHabitTracker account": "Účet OpenHabitTracker",
+ "OpenHabitTracker initial username": "Počiatočné používateľské meno OpenHabitTracker",
"OpenList": "OpenList",
"OpenObserve administrator": "Správca OpenObserve",
"OpenShot Video Editor is an award-winning free and open-source video editor for Linux, Mac, and Windows, and is dedicated to delivering high quality video editing and animation solutions to the world.": "OpenShot Video Editor je ocenený bezplatný a open-source video editor pre Linux, Mac a Windows a je venovaný poskytovaniu kvalitných riešení pre editáciu videa a animácie svetu.",
@@ -5216,6 +5249,7 @@
"Primary upstream DNS server": "Primárny nadradený server DNS",
"Privacy-first finance app with envelope budgeting and multi-device sync.": "Finančná aplikácia s dôrazom na súkromie, obálkovým rozpočtom a synchronizáciou viacerých zariadení.",
"Privacy-first, self-hosted PDF toolkit": "Súprava nástrojov PDF na vlastnom serveri s dôrazom na súkromie",
+ "Privacy-focused, self-hosted web analytics.": "Webová analytika s dôrazom na ochranu súkromia, ktorú prevádzkujete na vlastnom serveri.",
"Private installation record saved": "Uložené súkromné záznamy o inštalácii",
"Private network assigned automatically:": "Automaticky pridelená súkromná sieť:",
"Private network kept, because other containers still use it:": "Súkromná sieť ponechaná, pretože ju stále používajú iné kontajnery:",
@@ -5339,6 +5373,7 @@
"Public UDP port clients connect to": "Verejný port UDP na pripojenie klientov",
"Public UDP port peers connect to": "Verejný port UDP na pripojenie účastníkov",
"Public URL of phpMyAdmin when it is served behind a reverse proxy": "Verejné URL phpMyAdmin, keď sa podáva za reverzným proxy",
+ "Public URL used to access Baserow (for example https://baserow.example.com)": "Verejná adresa URL na prístup k Baserow (napríklad https://baserow.example.com)",
"Public address clients connect to (vpn.example.com or a public IP)": "Verejná adresa na pripojenie klientov (vpn.example.com alebo verejná IP adresa)",
"Public address peers connect to, or auto to detect it (vpn.example.com)": "Verejná adresa, ku ktorej sa pripájajú účastníci, alebo auto na jej zistenie (vpn.example.com)",
"Publish each directory from Resources: open it, switch on SMB, NFS or both, and save.": "Zverejnite každý adresár zo zdrojov: otvorte ho, zapnite SMB, NFS alebo oboje a uložte.",
@@ -5364,6 +5399,7 @@
"RAM in MiB": "RAM v MiB",
"REPAIR SUMMARY": "ZHRNUTIE OPRAVY",
"REQUIREMENTS:": "POŽIADAVKY:",
+ "REST API Basic Authentication user": "Používateľ základného overenia REST API",
"ROCm does not support this GPU officially. The application can use it by presenting it as another generation of its family: it is faster than the CPU, but under load the GPU can stop responding, and then the application does not answer until its container is restarted. The ROCm image is also larger than the others.": "ROCm túto grafickú kartu oficiálne nepodporuje. Aplikácia ju môže použiť tak, že sa karta predstaví ako iná generácia z rovnakej rodiny. Rozpoznávanie tým môže byť rýchlejšie než na CPU, ale pri zaťažení môže GPU prestať reagovať. Aplikácia potom nebude odpovedať, kým sa jej kontajner nereštartuje. Obraz ROCm je navyše väčší než ostatné obrazy.",
"ROCm requires /dev/kfd on the host": "ROCm vyžaduje /dev/kfd na hostiteľovi",
"ROCm requires the render device of an AMD GPU": "ROCm vyžaduje vykresľovacie zariadenie AMD GPU",
@@ -5674,6 +5710,7 @@
"Repositories switched to no-subscription": "Repozitáre prepnuté na no-subscription",
"Repository check failed. NVIDIA installation stopped.": "Kontrola úložiska zlyhala. Inštalácia NVIDIA sa zastavila.",
"Repository check failed. Update stopped.": "Kontrola úložiska zlyhala. Aktualizácia zastavená.",
+ "Repository encryption password": "Heslo na šifrovanie úložiska",
"Repository helper unavailable. NVIDIA installation stopped.": "Pomocník úložiska nie je k dispozícii. Inštalácia NVIDIA sa zastavila.",
"Repository policy returned an unexpected result.": "Politika úložiska vrátila neočakávaný výsledok.",
"Repository ready.": "Repozitár je pripravený.",
@@ -5791,6 +5828,7 @@
"Root shell/profile config": "Nastavenie shellu/profilu roota",
"Root user on the PVE host (default 'root'):": "Root používateľ na PVE hostovi (predvolené 'root'):",
"Rootfs size in GB": "Rootfs veľkosť v GB",
+ "Rotate and write /var/log to disk when it passes 85% of its size?": "Rotovať a zapísať /var/log na disk, keď prekročí 85 % svojej veľkosti?",
"Rotate the recovery passphrase": "Zmeniť obnovovaciu frázu",
"Routing Information": "Informácie o smerovaní",
"Routing Table": "Smerovacia tabuľka",
@@ -6263,6 +6301,7 @@
"Set ownership and permissions:": "Nastaviť vlastníka a oprávnenia:",
"Set the initial Technitium DNS administrator password": "Nastavte počiatočné heslo správcu Technitium DNS",
"Set this disk as the primary boot disk?": "Nastaviť tento disk ako hlavný bootovací disk?",
+ "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Nastavte na 1 iba v súkromnej LAN používajúcej výhradne HTTP; inak použite HTTPS.",
"Set up custom MOTD banner": "Nastaviť vlastnú MOTD správu",
"Set up the apex group and udev rules": "Nastaví skupinu apex a udev pravidlá",
"Set up time synchronization and entropy generation": "Nastaviť synchronizáciu času a generovanie entropie",
@@ -6967,6 +7006,7 @@
"The domain must resolve to the public address of this network before the certificate can be issued.": "Pred vydaním certifikátu musí doména smerovať na verejnú adresu tejto siete.",
"The download client still needs to be configured.": "Klienta na sťahovanie ešte treba nastaviť.",
"The download stopped progressing; cancelling this attempt.": "Sťahovanie prestalo napredovať; tento pokus sa ruší.",
+ "The downloaded Log2RAM does not match the expected release": "Stiahnutý Log2RAM nezodpovedá očakávanej verzii",
"The downloaded image does not match its manifest": "Stiahnutý obrázok nezodpovedá jeho manifestu",
"The downloaded image is corrupt:": "Stiahnutý obraz je poškodený:",
"The dpkg package database is clean.": "Databáza balíkov dpkg je čistá.",
@@ -7116,6 +7156,7 @@
"The oci-compose is incomplete: it does not say which image to install.": "oci-compose je neúplný: nehovorí, ktorý obraz sa má nainštalovať.",
"The oci-compose is not valid JSON:": "oci-compose nie je platný JSON:",
"The official Tandoor startup executable is missing": "Oficiálny spúšťač Tandoor chýba",
+ "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Oficiálne služby Umami a PostgreSQL v Compose nevyžadujú privilegovaný režim ani zmiernenie izolácie hostiteľa.",
"The official inventory contains no NVIDIA devices": "Oficiálny inventár neobsahuje žiadne zariadenia NVIDIA",
"The official inventory contains no NVIDIA driver components": "Oficiálny inventár neobsahuje žiadne komponenty ovládača NVIDIA",
"The official startup cannot be reproduced": "Oficiálny štart nie je možné reprodukovať",
@@ -7536,6 +7577,7 @@
"Total routes": "Počet trás",
"Total size:": "Celková veľkosť:",
"Toxiproxy": "Toxiproxy",
+ "Track movies, television, anime and games you want to enjoy.": "Sledujte filmy, seriály, anime a hry, ktoré si chcete pozrieť alebo zahrať.",
"Track subscriptions and recurring expenses.": "Sledovanie predplatných a opakovaných výdavkov.",
"Track your music listening history on your own server.": "Sledujte históriu počúvania hudby na vlastnom serveri.",
"Track your reading, books and history in a private library.": "Sledujte svoje čítanie, knihy a históriu v súkromnej knižnici.",
@@ -7615,6 +7657,7 @@
"Uninstalling Ceph...": "Odinštalúvam Ceph...",
"Uninstalling Fastfetch...": "Odinštalúvam Fastfetch...",
"Uninstalling Figurine...": "Odinštalúvam Figurine...",
+ "Uninstalling Log2RAM...": "Odinštalúvam Log2RAM...",
"Uninstalling amdgpu_top...": "Odinštalúvam amdgpu_top...",
"Uninstalling current NVIDIA driver before installing new version": "Pred inštaláciou novej verzie odinštalúvam aktuálny ovládač NVIDIA",
"Uninstalling intel-gpu-tools...": "Odinštalúvam intel-gpu-tools...",
@@ -8093,6 +8136,7 @@
"Warning: both VMs have autostart enabled (onboot=1).": "Upozornenie: obe VM majú zapnutý autostart (onboot=1).",
"Warnings": "Upozornenia",
"Watch history, ratings and watchlists synced between media servers and trackers.": "História sledovania, hodnotenia a zoznamy sledovaných synchronizované medzi mediálnymi servermi a trackermi.",
+ "Watcharr": "Watcharr",
"Watchdog": "Watchdog",
"Watchdog (off): restart the application when it crashes": "Watchdog (vypnutý): reštartovať aplikáciu, keď zlyhá",
"Watchdog (on): restart the application when it crashes": "Watchdog (zapnutý): reštartovať aplikáciu, keď zlyhá",
@@ -8870,10 +8914,5 @@
"⚠ Disk data will NOT be erased.": "⚠ Dáta na disku sa NEVYMAŽÚ.",
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Disk sa odpojí a odstráni z /etc/fstab.",
"⚠ The /etc/fstab entry will be removed.": "⚠ Záznam v /etc/fstab bude odstránený.",
- "⚠ The disk will be unmounted.": "⚠ Disk bude odpojený.",
- "Initial Umami administrator": "Počiatočný správca Umami",
- "Privacy-focused, self-hosted web analytics.": "Webová analytika s dôrazom na ochranu súkromia, ktorú prevádzkujete na vlastnom serveri.",
- "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "Oficiálne služby Umami a PostgreSQL v Compose nevyžadujú privilegovaný režim ani zmiernenie izolácie hostiteľa.",
- "A self-hosted, no-code database and application platform.": "Databázová a aplikačná platforma bez kódu, ktorú si hostujete sami.",
- "Public URL used to access Baserow (for example https://baserow.example.com)": "Verejná adresa URL na prístup k Baserow (napríklad https://baserow.example.com)"
+ "⚠ The disk will be unmounted.": "⚠ Disk bude odpojený."
}
diff --git a/lang/sv.json b/lang/sv.json
index ee25a507..ceed8d07 100644
--- a/lang/sv.json
+++ b/lang/sv.json
@@ -47,6 +47,7 @@
"A GPS tracking platform with device management and fleet reports.": "En plattform för GPS-spårning med enhetshantering och fordonsrapporter.",
"A GTK Broadway web UI for libvirt and virt-manager.": "En GTK Broadway webb UI för libvirt och virt-manager.",
"A Personal Relationship Management tool to help you document your social life.": "Ett personligt förhållande Management verktyg för att hjälpa dig att dokumentera ditt sociala liv.",
+ "A REST backend for restic backups; it has no web interface.": "En REST-backend för restic-säkerhetskopior utan webbgränssnitt.",
"A VirtIO ISO already exists. Do you want to overwrite it?": "En VirtIO ISO finns redan. Vill du skriva över det?",
"A ZFS pool with this name already exists.": "En ZFS-pool med detta namn finns redan.",
"A ZFS pool with this name already exists:": "En ZFS-pool med detta namn finns redan:",
@@ -94,6 +95,7 @@
"A lightweight dashboard that brings feeds, services and useful widgets together.": "En lättviktig instrumentpanel som samlar flöden, tjänster och användbara widgetar.",
"A lightweight hub for monitoring servers and containers.": "Ett lättviktigt nav för övervakning av servrar och containrar.",
"A lightweight self-hosted Kanban board.": "En lättviktig Kanban-tavla som du driftar själv.",
+ "A lightweight self-hosted URL shortener with SQLite storage.": "En lättviktig, egenhostad URL-förkortare med SQLite-lagring.",
"A lightweight self-hosted music server with Subsonic API support.": "En lättviktig musikserver som du driftar själv och som stöder Subsonic API.",
"A lightweight self-hosted pastebin for text and files.": "En lättviktig tjänst som du driftar själv för att dela text och filer.",
"A lightweight static homepage for organizing self-hosted services.": "En lättviktig statisk startsida för att organisera tjänster som du driftar själv.",
@@ -124,6 +126,7 @@
"A pending operation exists for": "En väntande operation finns för",
"A pending stack assembly already exists; it is not overwritten": "En väntande stack församling existerar redan; den är inte överskriven",
"A personal homepage for your apps and dashboards.": "En personlig startsida för dina appar och instrumentpaneler.",
+ "A personal, self-hosted microblog and timeline.": "En personlig, egenhostad mikroblogg och tidslinje.",
"A previous NVIDIA refresh is pending review": "En tidigare NVIDIA-uppdatering väntas granska",
"A previous VFIO passthrough configuration was detected for the following NVIDIA GPU(s):": "En tidigare VFIO-passthrough-konfiguration upptäcktes för följande NVIDIA-GPU:er:",
"A privacy-first, open-source platform for knowledge management and collaboration.": "En sekretess-först, open-source plattform för kunskapshantering och samarbete.",
@@ -147,15 +150,18 @@
"A self-hosted Q&A and knowledge-sharing platform.": "En plattform som du driftar själv för frågor och svar samt kunskapsdelning.",
"A self-hosted RSS reader inspired by Google Reader.": "Självhostad RSS-läsare inspirerad av Google Reader.",
"A self-hosted app for organizing recurring tasks and household chores, with data stored in SQLite.": "Självhostad app för att organisera återkommande uppgifter och hushållssysslor, med lagring i SQLite.",
+ "A self-hosted app for organizing tabletop role-playing campaigns.": "En egenhostad app för att organisera kampanjer för bordsrollspel.",
"A self-hosted bookmark manager and web archiver.": "En bokmärkeshanterare och webbariverare som du driftar själv.",
"A self-hosted bookmark manager.": "Självhostad bokmärkeshanterare.",
"A self-hosted file sharing service with expiring downloads.": "Självhostad fildelningstjänst med tidsbegränsade nedladdningar.",
"A self-hosted link-in-bio page.": "Självhostad länksida för sociala medier-profiler.",
"A self-hosted read-it-later application.": "En app som du driftar själv för att spara artiklar till senare.",
"A self-hosted task manager for planning work and tracking time.": "En uppgiftshanterare som du driftar själv för att planera arbete och registrera tid.",
+ "A self-hosted tracker for habits, tasks, notes and goals.": "En egenhostad spårare för vanor, uppgifter, anteckningar och mål.",
"A self-hosted vehicle maintenance and fuel log.": "En loggbok som du driftar själv för fordonsunderhåll och bränsleförbrukning.",
"A self-hosted workflow scheduler with a web interface.": "Självhostad arbetsflödesplanerare med webbgränssnitt.",
"A self-hosted, goal-free habit tracking tool.": "En självhäftad, målfri vana spårningsverktyg.",
+ "A self-hosted, no-code database and application platform.": "Självhostad plattform för databaser och applikationer utan kodning.",
"A self-improving AI agent with memory, skills, messaging, and a web dashboard.": "En självförbättrande AI-agent med minne, färdigheter, meddelanden och en webbdashboard.",
"A server reboot is recommended for all changes to take full effect.": "En omstart av servern rekommenderas för att alla ändringar ska få full effekt.",
"A shared directory was replaced during the installation": "En delad katalog ersattes under installationen",
@@ -276,6 +282,7 @@
"Active ZRAM algorithm, capacity or swap sources did not match the profile": "Aktiv ZRAM-algoritm, kapacitet eller swapkällor matchade inte profilen",
"Active exports:": "Aktiv export:",
"Active session:": "Aktiv session:",
+ "Activepieces": "Activepieces",
"Actual device path on the host": "Verklig enhetsväg på värden",
"Adaptation file too large": "Anpassningsfil för stor",
"Adaptation file with unexpected permissions or owner": "Anpassningsfil med oväntade behörigheter eller ägare",
@@ -783,6 +790,8 @@
"Build and install the gasket and apex kernel modules (DKMS)": "Bygg och installera kärnmodulerna gasket och apex (DKMS)",
"Build dependencies installed.": "Byggberoenden installerade.",
"Build dynamic websites using SQL files.": "Skapa dynamiska webbplatser med SQL-filer.",
+ "Build reports and dashboards from your data.": "Skapa rapporter och instrumentpaneler från dina data.",
+ "Build visual automations for personal and hobby use.": "Skapa visuella automatiseringar för personligt bruk och hobbyprojekt.",
"Build your personal knowledge base with TriliumNext Notes": "Bygg din personliga kunskapsbas med TriliumNext Notes",
"Bulk generation requires GITHUB_TOKEN to obtain an immutable revision per application": "Massgenerering kräver GITHUB_TOKEN för att hämta en oföränderlig revision per applikation",
"Business & ERP": "Företag och ERP",
@@ -827,6 +836,7 @@
"Calibre-web is a web app providing a clean interface for browsing, reading and downloading eBooks using an existing Calibre database. It is also possible to integrate google drive and edit metadata and your calibre library through the app itself.": "Calibre-web är en webbapp som ger ett rent gränssnitt för att surfa, läsa och ladda ner e-böcker med en befintlig Calibre-databas. Det är också möjligt att integrera Google Drive och redigera metadata och ditt calibre-bibliotek genom själva appen.",
"Calligra is an office and graphic art suite by KDE. It is available for desktop PCs, tablet computers, and smartphones. It contains applications for word processing, spreadsheets, presentation, vector graphics, and editing databases.": "Calligra är ett kontor och grafisk konstsvit av KDE. Det är tillgängligt för stationära datorer, surfplattor och smartphones. Den innehåller applikationer för ordbehandling, kalkylblad, presentation, vektorgrafik och redigeringsdatabaser.",
"Calnode": "Calnode",
+ "Campfire first-run setup": "Första konfigurationen av Campfire",
"Cancel": "Avbryt",
"Cancel restore": "Avbryt återställning",
"Cancel this setup": "Avbryt denna inställning",
@@ -974,6 +984,8 @@
"Checklist post-upgrade finished. Warnings:": "Checklista efter uppgradering avslutad. Varningar:",
"Checklist pre-check finished. Warnings:": "Checklista förhandskontroll avslutad. Varningar:",
"Checks for LVM and storage issues": "Kontrollerar LVM och lagringsproblem",
+ "Chhoto URL": "Chhoto URL",
+ "Chhoto URL administrator password": "Administratörslösenord för Chhoto URL",
"Choose": "Välj",
"Choose BIOS Type": "Välj BIOS-typ",
"Choose BIOS type": "Välj BIOS-typ",
@@ -1192,6 +1204,7 @@
"Configured during installation": "Konfigureras vid installationen",
"Configured import disk %s as %s": "Importdisken %s konfigurerad som %s",
"Configured loader disk as ide0": "Loader-disken konfigurerad som ide0",
+ "Configured username": "Konfigurerat användarnamn",
"Configured virtual disk as %s, %sGB on %s": "Virtuell disk konfigurerad som %s, %sGB på %s",
"Configuring AMD GPU passthrough...": "Konfigurerar AMD GPU-passthrough...",
"Configuring AMD softdep...": "Konfigurerar AMD softdep...",
@@ -1601,6 +1614,7 @@
"Create and stream custom live TV channels from your media library.": "Skapa och strömma egna direktsända TV-kanaler från ditt mediebibliotek.",
"Create credentials file (recommended):": "Skapa autentiseringsfil (rekommenderas):",
"Create directory": "Skapa katalog",
+ "Create encrypted, deduplicated backups and restore files.": "Skapa krypterade, deduplicerade säkerhetskopior och återställ filer.",
"Create export directory:": "Skapa exportkatalog:",
"Create mount point:": "Skapa monteringspunkt:",
"Create new directory in /mnt": "Skapa ny katalog i /mnt",
@@ -1611,9 +1625,11 @@
"Create share directory:": "Skapa delningskatalog:",
"Create shared directory:": "Skapa delad katalog:",
"Create this LXC in privileged mode?": "Skapa denna LXC i privilegierat läge?",
+ "Create with create_user in the container console": "Skapa användaren med create_user i containerns konsol",
"Create, send and sign documents with a self-hosted web app.": "Skapa, skicka och signera dokument med en webbapp som du driftar själv.",
"Created common remapped user": "Skapat gemensam ommappad användare",
"Created directory on host:": "Skapat katalog på värd:",
+ "Created during first-run setup": "Skapas under den första konfigurationen",
"Created persistent names for": "Skapat beständiga namn för",
"Created remapped user": "Skapat ommappad användare",
"Created sharedfiles group (GID: 101000)": "Skapade sharedfiles-grupp (GID: 101000)",
@@ -2127,6 +2143,7 @@
"Each LUN will appear as a block device assignable to VMs.": "Varje LUN kommer att visas som en blockenhet som kan tilldelas virtuella datorer.",
"Each peer gets its configuration and its QR code inside the container: /config/peer1/peer1.conf and /config/peer1/peer1.png, or /config/peer_/peer_.conf when names were given.": "Varje peer får sin konfiguration och sin QR-kod inuti behållaren: /config/peer1/peer1.conf och/config/peer1/peer1.png eller/config/peer /peer .conf när namnen gavs.",
"Ebook and audiobook collection manager for Usenet and BitTorrent users.": "Ebook och ljudbok samlingschef för Usenet och BitTorrent användare.",
+ "Ech0": "Ech0",
"Eden is an experimental open-source emulator for the Nintendo Switch, built with performance and stability in mind.": "Eden är en experimentell open-source emulator för Nintendo Switch, byggd med prestanda och stabilitet i åtanke.",
"Edge TPU runtime installed.": "Edge TPU runtime installerad.",
"Edit": "Redigera",
@@ -2136,6 +2153,7 @@
"Edit the VM machine type to q35 and try again.": "Redigera VM-maskintypen till q35 och försök igen.",
"Edition:": "Utgåva:",
"Email address for certificate expiry notices (required by ZeroSSL)": "E-postadress för certifikatutgångsmeddelanden (required av ZeroSSL)",
+ "Email address for the OpenHabitTracker account": "E-postadress för OpenHabitTracker-kontot",
"Email address of the LibreDB Studio administrator": "E-postadress till LibreDB Studio-administratören",
"Email address of the NetBox admin account": "E-postadress för NetBox administratörskonto",
"Email address of the admin account": "E-postadressen till administratörskontot",
@@ -2299,6 +2317,7 @@
"Enter the BORG REPOKEY passphrase for target:": "Ange BORG REPOKEY-lösenfrasen för målet:",
"Enter the Samba username:": "Ange Samba användarnamn:",
"Enter the VMID for the new VM:": "Ange VMID för den nya virtuella datorn:",
+ "Enter the base URL clients and webhook providers will use to reach this Activepieces instance.": "Ange bas-URL:en som klienter och webhook-leverantörer ska använda för att nå den här Activepieces-instansen.",
"Enter the container terminal:": "Gå in i containerterminalen:",
"Enter the full path to the directory containing disk images:": "Ange den fullständiga sökvägen till katalogen som innehåller diskbilder:",
"Enter the full path to the host directory:": "Ange den fullständiga sökvägen till värdkatalogen:",
@@ -2668,7 +2687,9 @@
"Firewall allows port": "Brandväggen tillåter port",
"Firewall settings": "Brandväggsinställningar",
"Firmware :": "Firmware:",
+ "First Ech0 account (becomes Owner)": "Första Ech0-kontot (blir ägare)",
"First Jelu administrator": "Första Jelu-administratören",
+ "First Watcharr account (becomes administrator)": "Första Watcharr-kontot (blir administratör)",
"First complete DSM setup and verify Web UI/SSH access.": "Slutför först DSM-installationen och verifiera webbgränssnitt/SSH-åtkomst.",
"First complete ZimaOS setup and verify remote access (web/SSH).": "Slutför först ZimaOS-installationen och verifiera fjärråtkomst (webb/SSH).",
"First install the GPU drivers inside the guest and verify remote access (RDP/SSH).": "Installera först GPU-drivrutinerna i gästen och verifiera fjärråtkomst (RDP/SSH).",
@@ -3306,6 +3327,7 @@
"Initial Redmine administrator": "Första Redmine-administratören",
"Initial Stirling-PDF account": "Första Stirling-PDF-kontot",
"Initial Tandoor administrator": "Initial Tandoor administratör",
+ "Initial Umami administrator": "Umamis första administratör",
"Initial administrator created:": "Initial administratör skapad:",
"Initial administrator user": "Initial administratör användare",
"Initializing Borg repository if needed...": "Initierar Borg-arkivet om det behövs...",
@@ -3404,6 +3426,7 @@
"Installing Fail2Ban...": "Installerar Fail2Ban...",
"Installing Git as a prerequisite...": "Installera Git som en förutsättning...",
"Installing Intel VA-API drivers in container...": "Installerar Intel VA-API-drivrutiner i container...",
+ "Installing Log2RAM": "Installerar Log2RAM",
"Installing Log2RAM from GitHub...": "Installerar Log2RAM från GitHub...",
"Installing Log2RAM from source...": "Installerar Log2RAM från källan...",
"Installing NFS Client in LXC": "Installerar NFS-klient i LXC",
@@ -3753,6 +3776,8 @@
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa är ett kraftfullt verktyg för att ge dig fullständig kontroll över dina mediebibliotek. Med Kometa kan du ta din anpassning till nästa nivå, med granulär kontroll över metadata, samlingar, överlagringar och mycket mer.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa läser sin konfiguration från /config/config.yml och behållaren endast fartyg /config/config.yml.template. Kopiera mallen till config.yml, fyll i required Plex och TMDb-anslutningar och starta sedan om behållaren.",
"Komga is a media server for your comics, mangas, BDs, magazines and eBooks.": "Komga är en mediaserver för dina serier, mangas, BD, tidskrifter och e-böcker.",
+ "Kopia Web UI": "Kopia-webbgränssnitt",
+ "Kopia Web UI login": "Inloggning till Kopias webbgränssnitt",
"Krita is a professional FREE and open source painting program. It is made by artists that want to see affordable art tools for everyone.": "Krita är ett professionellt gratis och öppen källkod målning program. Den är gjord av konstnärer som vill se prisvärda konstverktyg för alla.",
"LAN access to the kept containers (stack configuration incomplete):": "LAN tillgång till de bevarade behållarna (stack konfiguration ofullständig):",
"LAN address applied to the application URLs": "LAN-adress tillämpad på ansökningsadresserna",
@@ -3894,10 +3919,14 @@
"Log aggregation server for Grafana.": "Loggaggregeringsserver för Grafana.",
"Log file": "Loggfil",
"Log2RAM already registered — updating to latest configuration": "Log2RAM redan registrerat — uppdaterar till senaste konfigurationen",
+ "Log2RAM could not be downloaded": "Log2RAM kunde inte laddas ner",
"Log2RAM installation and configuration completed successfully.": "Log2RAM-installationen och konfigurationen har slutförts framgångsrikt.",
"Log2RAM installation cancelled by user": "Log2RAM-installationen avbröts av användaren",
"Log2RAM installation verification failed. Check /tmp/log2ram_install.log": "Verifiering av Log2RAM-installationen misslyckades. Kontrollera /tmp/log2ram_install.log",
"Log2RAM installed successfully": "Log2RAM installerades framgångsrikt",
+ "Log2RAM installed:": "Log2RAM installerat:",
+ "Log2RAM is removed at the next reboot, after writing /var/log to disk": "Log2RAM tas bort vid nästa omstart, efter att /var/log har skrivits till disken",
+ "Log2RAM keeps /var/log in RAM; the journal stays on disk from the next boot": "Log2RAM håller /var/log i RAM; journalen ligger kvar på disken från nästa start",
"Log2RAM keeps the journal in order from the next reboot": "Log2RAM håller ordning på journalen från nästa omstart",
"Log2RAM size set to:": "Log2RAM-storlek inställd på:",
"Log2RAM write scheduled every": "Log2RAM-skrivning schemalagt varje",
@@ -3980,6 +4009,7 @@
"Manage custom backup paths": "Hantera anpassade säkerhetskopieringsvägar",
"Manage custom paths (add / remove your folders)": "Hantera anpassade sökvägar (lägg till / ta bort dina mappar)",
"Manage data and APIs with a modern visual interface.": "Hantera data och API:er med ett modernt visuellt gränssnitt.",
+ "Manage database changes, access and workflows.": "Hantera databasändringar, åtkomst och arbetsflöden.",
"Manage installed OCI applications": "Hantera installerade OCI-program",
"Manage local backup target": "Hantera lokalt mål för säkerhetskopiering",
"Manage private image registries.\nSaved credentials let ProxMenux access your private images.": "Hantera privata bildregister.\nSparade referenser låter ProxMenux komma åt dina privata bilder.",
@@ -4847,6 +4877,9 @@
"Open-source in-memory data store.": "Öppen datalagring i minnet.",
"Open-source self-hosted SQL IDE.": "Open-source självvärd SQL IDE.",
"OpenClaw is a personal AI assistant you run on your own devices": "OpenClaw är en personlig AI-assistent du kör på dina egna enheter",
+ "OpenHabitTracker": "OpenHabitTracker",
+ "OpenHabitTracker account": "OpenHabitTracker-konto",
+ "OpenHabitTracker initial username": "Första användarnamnet för OpenHabitTracker",
"OpenList": "OpenList",
"OpenObserve administrator": "OpenObserve-administratör",
"OpenShot Video Editor is an award-winning free and open-source video editor for Linux, Mac, and Windows, and is dedicated to delivering high quality video editing and animation solutions to the world.": "OpenShot Video Editor är en prisbelönt gratis och open-source videoredigerare för Linux, Mac och Windows, och är dedikerad till att leverera högkvalitativa videoredigerings- och animationslösningar till världen.",
@@ -5214,6 +5247,7 @@
"Primary upstream DNS server": "Primär uppströms DNS-server",
"Privacy-first finance app with envelope budgeting and multi-device sync.": "Privacy-first finans app med kuvert budgeting och multi-device sync.",
"Privacy-first, self-hosted PDF toolkit": "Privacy-first, självhäftad PDF-verktyg",
+ "Privacy-focused, self-hosted web analytics.": "Självhostad webbanalys med fokus på integritet.",
"Private installation record saved": "Privat installationsrekord sparad",
"Private network assigned automatically:": "Privat nätverk tilldelas automatiskt:",
"Private network kept, because other containers still use it:": "Privat nätverk behålls, eftersom andra behållare fortfarande använder det:",
@@ -5337,6 +5371,7 @@
"Public UDP port clients connect to": "Offentliga UDP-portklienter ansluter till",
"Public UDP port peers connect to": "Offentliga UDP-portpeers ansluter till",
"Public URL of phpMyAdmin when it is served behind a reverse proxy": "Public URL av phpMyAdmin när den serveras bakom en omvänd proxy",
+ "Public URL used to access Baserow (for example https://baserow.example.com)": "Offentlig URL för åtkomst till Baserow (till exempel https://baserow.example.com)",
"Public address clients connect to (vpn.example.com or a public IP)": "Offentliga adressklienter ansluter till (vpn.example.com eller en offentlig IP)",
"Public address peers connect to, or auto to detect it (vpn.example.com)": "Offentliga adresspeers ansluter till, eller auto för att upptäcka det (vpn.example.com)",
"Publish each directory from Resources: open it, switch on SMB, NFS or both, and save.": "Publicera varje katalog från Resurser: öppna den, slå på SMB, NFS eller båda och spara.",
@@ -5362,6 +5397,7 @@
"RAM in MiB": "RAM i MiB",
"REPAIR SUMMARY": "REPARATION SAMMANFATTNING",
"REQUIREMENTS:": "KRAV:",
+ "REST API Basic Authentication user": "Användare för REST API:s grundläggande autentisering",
"ROCm does not support this GPU officially. The application can use it by presenting it as another generation of its family: it is faster than the CPU, but under load the GPU can stop responding, and then the application does not answer until its container is restarted. The ROCm image is also larger than the others.": "ROCm stöder inte denna GPU officiellt. Applikationen kan använda den genom att presentera den som en annan generation i samma familj: den är snabbare än CPU, men under belastning kan GPU sluta svara, och då svarar applikationen inte förrän dess behållare har startats om. ROCm-avbilden är också större än de andra.",
"ROCm requires /dev/kfd on the host": "ROCm kräver /dev/kfd på värden",
"ROCm requires the render device of an AMD GPU": "ROCm kräver renderingsenheten för en AMD GPU",
@@ -5672,6 +5708,7 @@
"Repositories switched to no-subscription": "Lagren bytte till ingen prenumeration",
"Repository check failed. NVIDIA installation stopped.": "Arkivkontrollen misslyckades. NVIDIA-installationen stoppades.",
"Repository check failed. Update stopped.": "Arkivkontrollen misslyckades. Uppdateringen stoppades.",
+ "Repository encryption password": "Lösenord för kryptering av arkivet",
"Repository helper unavailable. NVIDIA installation stopped.": "Arkivhjälparen är inte tillgänglig. NVIDIA-installationen stoppades.",
"Repository policy returned an unexpected result.": "Arkivpolicyn gav ett oväntat resultat.",
"Repository ready.": "Förvaret klart.",
@@ -5789,6 +5826,7 @@
"Root shell/profile config": "Rotskal/profilkonfiguration",
"Root user on the PVE host (default 'root'):": "Rotanvändare på PVE-värden (standard 'root'):",
"Rootfs size in GB": "Rootfs storlek i GB",
+ "Rotate and write /var/log to disk when it passes 85% of its size?": "Rotera och skriva /var/log till disken när den överstiger 85 % av sin storlek?",
"Rotate the recovery passphrase": "Vrid återställningslösenfrasen",
"Routing Information": "Routningsinformation",
"Routing Table": "Rutttabell",
@@ -6261,6 +6299,7 @@
"Set ownership and permissions:": "Ange äganderätt och behörigheter:",
"Set the initial Technitium DNS administrator password": "Ange det första administratörslösenordet för Technitium DNS",
"Set this disk as the primary boot disk?": "Vill du ställa in den här disken som primär startdiskett?",
+ "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise.": "Ange 1 endast för ett privat nätverk som enbart använder HTTP; använd annars HTTPS.",
"Set up custom MOTD banner": "Konfigurera anpassad MOTD-banner",
"Set up the apex group and udev rules": "Ställ in apexgruppen och udev-reglerna",
"Set up time synchronization and entropy generation": "Ställ in tidssynkronisering och generering av entropi",
@@ -6966,6 +7005,7 @@
"The domain must resolve to the public address of this network before the certificate can be issued.": "Domänen måste lösa den offentliga adressen till detta nätverk innan certifikatet kan utfärdas.",
"The download client still needs to be configured.": "Nedladdningskunden behöver fortfarande konfigureras.",
"The download stopped progressing; cancelling this attempt.": "Nedladdningen slutade utvecklas; avbryta detta försök.",
+ "The downloaded Log2RAM does not match the expected release": "Det nedladdade Log2RAM motsvarar inte den förväntade versionen",
"The downloaded image does not match its manifest": "Den nedladdade bilden matchar inte dess manifest",
"The downloaded image is corrupt:": "Den nedladdade bilden är korrupt:",
"The dpkg package database is clean.": "Paketdatabasen för dpkg är ren.",
@@ -7115,6 +7155,7 @@
"The oci-compose is incomplete: it does not say which image to install.": "oci-compose är ofullständig: den anger inte vilken avbild som ska installeras.",
"The oci-compose is not valid JSON:": "oci-compose är inte giltig JSON:",
"The official Tandoor startup executable is missing": "Den officiella Tandoor start körbar saknas",
+ "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "De officiella Compose-tjänsterna för Umami och PostgreSQL begär varken privilegierat läge eller en mindre strikt isolering av värdsystemet.",
"The official inventory contains no NVIDIA devices": "Den officiella inventeringen innehåller inga NVIDIA-enheter",
"The official inventory contains no NVIDIA driver components": "Den officiella inventeringen innehåller inga NVIDIA-komponenter",
"The official startup cannot be reproduced": "Den officiella starten kan inte reproduceras",
@@ -7535,6 +7576,7 @@
"Total routes": "Totalt antal rutter",
"Total size:": "Total storlek:",
"Toxiproxy": "Toxiproxy",
+ "Track movies, television, anime and games you want to enjoy.": "Håll koll på filmer, tv-serier, anime och spel som du vill se eller spela.",
"Track subscriptions and recurring expenses.": "Håll koll på prenumerationer och återkommande utgifter.",
"Track your music listening history on your own server.": "Följ din musikhistorik på din egen server.",
"Track your reading, books and history in a private library.": "Håll ordning på läsning, böcker och historik i ett privat bibliotek.",
@@ -7614,6 +7656,7 @@
"Uninstalling Ceph...": "Avinstallerar Ceph...",
"Uninstalling Fastfetch...": "Avinstallerar Fastfetch...",
"Uninstalling Figurine...": "Avinstallerar figuren...",
+ "Uninstalling Log2RAM...": "Avinstallerar Log2RAM...",
"Uninstalling amdgpu_top...": "Avinstallerar amdgpu_top...",
"Uninstalling current NVIDIA driver before installing new version": "Avinstallerar nuvarande NVIDIA-drivrutin innan den nya versionen installeras",
"Uninstalling intel-gpu-tools...": "Avinstallerar intel-gpu-tools...",
@@ -8092,6 +8135,7 @@
"Warning: both VMs have autostart enabled (onboot=1).": "Varning: båda virtuella datorerna har autostart aktiverad (onboot=1).",
"Warnings": "Varningar",
"Watch history, ratings and watchlists synced between media servers and trackers.": "Visningshistorik, betyg och bevakningslistor synkade mellan mediaservrar och spårare.",
+ "Watcharr": "Watcharr",
"Watchdog": "Watchdog",
"Watchdog (off): restart the application when it crashes": "Watchdog (av): starta om applikationen när den kraschar",
"Watchdog (on): restart the application when it crashes": "Watchdog (på): starta om applikationen när den kraschar",
@@ -8869,10 +8913,5 @@
"⚠ Disk data will NOT be erased.": "⚠ Diskdata kommer INTE att raderas.",
"⚠ Disk will be unmounted and removed from /etc/fstab.": "⚠ Disken kommer att avmonteras och tas bort från /etc/fstab.",
"⚠ The /etc/fstab entry will be removed.": "⚠ /etc/fstab-posten kommer att tas bort.",
- "⚠ The disk will be unmounted.": "⚠ Disken kommer att avmonteras.",
- "Initial Umami administrator": "Umamis första administratör",
- "Privacy-focused, self-hosted web analytics.": "Självhostad webbanalys med fokus på integritet.",
- "The official Umami and PostgreSQL Compose services do not request privileged mode or relaxed host confinement.": "De officiella Compose-tjänsterna för Umami och PostgreSQL begär varken privilegierat läge eller en mindre strikt isolering av värdsystemet.",
- "A self-hosted, no-code database and application platform.": "Självhostad plattform för databaser och applikationer utan kodning.",
- "Public URL used to access Baserow (for example https://baserow.example.com)": "Offentlig URL för åtkomst till Baserow (till exempel https://baserow.example.com)"
+ "⚠ The disk will be unmounted.": "⚠ Disken kommer att avmonteras."
}
diff --git a/oci/VALIDATION.md b/oci/VALIDATION.md
index a3ff01e1..58bf4135 100644
--- a/oci/VALIDATION.md
+++ b/oci/VALIDATION.md
@@ -2,7 +2,7 @@
-43 of 516 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 2 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
+43 of 526 applications in the OCI catalog have been tested for real: 41 in the ProxMenux lab and 2 by the community. The OCI installer shows them as verified, with a ✓ in the lists.
Each test describes the scenario that was run and names the image it ran on. It does not cover every possible configuration of the application, and a newer image has not been tested until someone reports it.
diff --git a/oci/catalog/apps/activepieces.json b/oci/catalog/apps/activepieces.json
new file mode 100644
index 00000000..a1c694b2
--- /dev/null
+++ b/oci/catalog/apps/activepieces.json
@@ -0,0 +1,524 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-activepieces",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Activepieces",
+ "es_ES": "Activepieces"
+ },
+ "tagline": {
+ "en_US": "Build visual automations for personal and hobby use.",
+ "es_ES": "Crea automatizaciones visuales para uso personal."
+ },
+ "description": {
+ "en_US": "Build and run automation flows from a self-hosted visual interface. This single-container profile uses Activepieces’ documented personal mode with embedded storage and an in-memory queue.",
+ "es_ES": "Diseña y ejecuta flujos de automatización desde una interfaz visual autoalojada. Este perfil de un solo contenedor usa el modo personal documentado de Activepieces, con almacenamiento integrado y cola en memoria."
+ },
+ "category": "automation",
+ "category_label": "Automation & Scheduling",
+ "author": "Activepieces",
+ "developer": "Activepieces",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/activepieces.webp",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 80,
+ "path": "/"
+ },
+ "website": "https://www.activepieces.com/",
+ "documentation": "https://www.activepieces.com/docs/install/options/docker",
+ "repository": "https://github.com/activepieces/activepieces",
+ "tips": [
+ "The first account you create becomes the administrator; Activepieces has no default username or password.",
+ "Use the base URL entered during setup. Webhooks and app triggers need it to be reachable by third parties.",
+ "This single-container mode uses embedded PGLite and an in-memory queue for personal use on one machine. It is not the production or multi-instance setup."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "activepieces",
+ "repository": "https://github.com/activepieces/activepieces",
+ "revision": "bc26934b0a08842ec4fd24d83ecd08907cbaec1e4d7a985d30138e4f67ea6855",
+ "image_repository_url": "https://github.com/activepieces/activepieces/pkgs/container/activepieces",
+ "readme_pushed_at": "",
+ "compose_sha256": "bc26934b0a08842ec4fd24d83ecd08907cbaec1e4d7a985d30138e4f67ea6855",
+ "generated_at": "2026-10-26T10:22:17+00:00",
+ "readme_raw_url": "https://raw.githubusercontent.com/activepieces/activepieces/main/README.md"
+ },
+ "container_contract": {
+ "service_name": "activepieces",
+ "container_name": "activepieces",
+ "image": {
+ "reference": "ghcr.io/activepieces/activepieces:latest",
+ "registry": "ghcr.io",
+ "repository": "ghcr.io/activepieces/activepieces",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "AP_DB_TYPE",
+ "example": "PGLITE",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "AP_REDIS_TYPE",
+ "example": "MEMORY",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "AP_FRONTEND_URL",
+ "example": "",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt_user": true,
+ "prompt": "Enter the base URL clients and webhook providers will use to reach this Activepieces instance."
+ },
+ {
+ "name": "AP_ENCRYPTION_KEY",
+ "example": "${GENERATED_AP_ENCRYPTION_KEY}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "AP_JWT_SECRET",
+ "example": "${GENERATED_AP_JWT_SECRET}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/root/.activepieces",
+ "compose_source_example": "./activepieces-data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 80,
+ "published_example": 80,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "name: activepieces\nservices:\n activepieces:\n environment:\n AP_DB_TYPE: PGLITE\n AP_ENCRYPTION_KEY: ${GENERATED_GENERATED_AP_ENCRYPTION_KEY}\n AP_FRONTEND_URL: ''\n AP_JWT_SECRET: ${GENERATED_GENERATED_AP_JWT_SECRET}\n AP_REDIS_TYPE: MEMORY\n image: ghcr.io/activepieces/activepieces:latest\n ports:\n - 80:80\n volumes:\n - ./.activepieces:/root/.activepieces\n"
+ },
+ "compose_stack": {
+ "project_name": "activepieces",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "activepieces",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "activepieces",
+ "image": "ghcr.io/activepieces/activepieces:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "environment": {
+ "AP_DB_TYPE": "PGLITE",
+ "AP_ENCRYPTION_KEY": "${GENERATED_GENERATED_AP_ENCRYPTION_KEY}",
+ "AP_FRONTEND_URL": "",
+ "AP_JWT_SECRET": "${GENERATED_GENERATED_AP_JWT_SECRET}",
+ "AP_REDIS_TYPE": "MEMORY"
+ },
+ "image": "ghcr.io/activepieces/activepieces:latest",
+ "ports": [
+ "80:80"
+ ],
+ "volumes": [
+ "./.activepieces:/root/.activepieces"
+ ]
+ }
+ }
+ ],
+ "top_level": {
+ "name": "activepieces"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "activepieces-volume-0",
+ "service": "activepieces",
+ "container_path": "/root/.activepieces",
+ "mode": "managed-volume",
+ "user_selectable": false,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "activepieces"
+ ],
+ "stop_order": [
+ "activepieces"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "generated-ap-encryption-key",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "activepieces",
+ "environment_variable": "AP_ENCRYPTION_KEY"
+ }
+ ]
+ },
+ {
+ "id": "generated-ap-jwt-secret",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "activepieces",
+ "environment_variable": "AP_JWT_SECRET"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Activepieces",
+ "scheme": "http",
+ "port": 80,
+ "path": "/",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": []
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 2048,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "volume_preparations": [
+ {
+ "container_path": "/root/.activepieces",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-application-user",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "generated_sensitive_environment": {
+ "AP_ENCRYPTION_KEY": {
+ "strategy": "token-hex",
+ "bytes": 16
+ },
+ "AP_JWT_SECRET": {
+ "strategy": "token-hex",
+ "bytes": 32
+ }
+ },
+ "minimum_secret_length": {
+ "AP_ENCRYPTION_KEY": 32,
+ "AP_JWT_SECRET": 32
+ },
+ "volume_owner": {
+ "uid": 0,
+ "gid": 0
+ },
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 80,
+ "path": "/api/v1/health",
+ "timeout_seconds": 300,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 5,
+ "verify_tls": false
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/apps/bytebase.json b/oci/catalog/apps/bytebase.json
new file mode 100644
index 00000000..7a9ee724
--- /dev/null
+++ b/oci/catalog/apps/bytebase.json
@@ -0,0 +1,445 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-bytebase",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Bytebase",
+ "es_ES": "Bytebase"
+ },
+ "tagline": {
+ "en_US": "Manage database changes, access and workflows.",
+ "es_ES": "Administra cambios, accesos y flujos de trabajo de bases de datos."
+ },
+ "description": {
+ "en_US": "Manage database changes, access and workflows through a browser-based workspace. The image contains its own database, so this profile does not require a second service.",
+ "es_ES": "Gestiona cambios, accesos y flujos de bases de datos desde un espacio de trabajo web. La imagen incluye su propia base de datos, por lo que este perfil no necesita otro servicio."
+ },
+ "category": "databases",
+ "category_label": "Databases",
+ "author": "Bytebase",
+ "developer": "Bytebase",
+ "icon": "https://cdn.jsdelivr.net/gh/bytebase/bytebase@main/frontend/src/assets/logo-icon.svg",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/"
+ },
+ "website": "https://www.bytebase.com/",
+ "documentation": "https://docs.bytebase.com/get-started/self-host/deploy-with-docker",
+ "repository": "https://github.com/bytebase/bytebase",
+ "tips": [
+ "On first visit, complete the web setup wizard to create the initial administrator and workspace; the image has no default login credentials.",
+ "To use Bytebase for database changes, connect the database instances you want to manage from its web interface.",
+ "En la primera visita, completa el asistente web para crear el administrador y el espacio de trabajo inicial; la imagen no tiene credenciales de acceso predeterminadas.",
+ "Para gestionar cambios de bases de datos, conecta desde la interfaz las instancias que quieras administrar."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "Bytebase",
+ "repository": "https://github.com/bytebase/bytebase",
+ "revision": "e77473a25aa12a669dc15bc33d37c276d7478b47",
+ "image_repository_url": "https://hub.docker.com/r/bytebase/bytebase",
+ "readme_pushed_at": "",
+ "compose_sha256": "b3756c0ae20c2a82b17f7cd04f116c4aef1b4a49d6ccf651bd865d74ab865e22",
+ "generated_at": "2026-10-26T09:44:16+00:00",
+ "readme_raw_url": "https://github.com/bytebase/bytebase",
+ "default_branch": "main"
+ },
+ "container_contract": {
+ "service_name": "bytebase",
+ "container_name": "bytebase",
+ "image": {
+ "reference": "bytebase/bytebase:latest",
+ "registry": "docker.io",
+ "repository": "bytebase/bytebase",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install"
+ },
+ "environment": [],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/var/opt/bytebase",
+ "compose_source_example": "bytebase-data-0",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 16
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 8080,
+ "published_example": 8080,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "{\n \"name\": \"bytebase\",\n \"services\": {\n \"bytebase\": {\n \"image\": \"bytebase/bytebase:latest\",\n \"restart\": \"unless-stopped\",\n \"command\": [\n \"--port\",\n \"8080\",\n \"--data\",\n \"/var/opt/bytebase\"\n ],\n \"ports\": [\n \"8080:8080/tcp\"\n ],\n \"volumes\": [\n \"bytebase-data-0:/var/opt/bytebase\"\n ]\n }\n },\n \"volumes\": {\n \"bytebase-data-0\": {}\n }\n}"
+ },
+ "compose_stack": {
+ "project_name": "bytebase",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "bytebase",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "bytebase",
+ "image": "bytebase/bytebase:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "bytebase/bytebase:latest",
+ "restart": "unless-stopped",
+ "command": [
+ "--port",
+ "8080",
+ "--data",
+ "/var/opt/bytebase"
+ ],
+ "ports": [
+ "8080:8080/tcp"
+ ],
+ "volumes": [
+ "bytebase-data-0:/var/opt/bytebase"
+ ]
+ }
+ }
+ ],
+ "top_level": {
+ "name": "bytebase",
+ "volumes": {
+ "bytebase-data-0": {}
+ }
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "bytebase-volume-0",
+ "service": "bytebase",
+ "container_path": "/var/opt/bytebase",
+ "mode": "managed-volume",
+ "user_selectable": false,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "bytebase"
+ ],
+ "stop_order": [
+ "bytebase"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": []
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Web UI",
+ "scheme": "http",
+ "port": 8080,
+ "path": "/",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": []
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 2048,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 60
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/healthz",
+ "timeout_seconds": 180,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false
+ },
+ "generated_sensitive_environment": {}
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "pending",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/apps/campfire.json b/oci/catalog/apps/campfire.json
new file mode 100644
index 00000000..6a93cb3f
--- /dev/null
+++ b/oci/catalog/apps/campfire.json
@@ -0,0 +1,457 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-campfire",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Campfire",
+ "es_ES": "Campfire"
+ },
+ "tagline": {
+ "en_US": "A self-hosted app for organizing tabletop role-playing campaigns.",
+ "es_ES": "Aplicación autoalojada para organizar partidas de rol de mesa."
+ },
+ "description": {
+ "en_US": "A self-hosted web application for organizing tabletop role-playing games.",
+ "es_ES": "Una aplicación web autoalojada para organizar campañas y sesiones de juegos de rol."
+ },
+ "category": "gaming",
+ "category_label": "Gaming & Leisure",
+ "author": "AwsomeFox",
+ "developer": "AwsomeFox",
+ "icon": "https://raw.githubusercontent.com/AwsomeFox/campfire/main/apps/web/public/pwa-512x512.png",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/auth/setup"
+ },
+ "website": "https://github.com/AwsomeFox/campfire",
+ "documentation": "https://github.com/AwsomeFox/campfire#readme",
+ "repository": "https://github.com/AwsomeFox/campfire",
+ "tips": [
+ "Create the first account at /auth/setup after startup.",
+ "For direct access over plain HTTP on a trusted LAN, set ALLOW_INSECURE_HTTP=1 in advanced settings. Leave it unset when using HTTPS."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "AwsomeFox",
+ "repository": "https://github.com/AwsomeFox/campfire",
+ "revision": "f0dd871f5d2ab0b949db8c8a15a90ec56d40d3dc",
+ "image_repository_url": "https://github.com/AwsomeFox/campfire/pkgs/container/campfire",
+ "readme_pushed_at": "",
+ "compose_sha256": "0cb682cac1fd76326b551b057b28256280b7d7a982f6f418526b39bf75f5da45",
+ "generated_at": "2026-10-26T06:55:28+00:00",
+ "readme_raw_url": "https://github.com/AwsomeFox/campfire#readme"
+ },
+ "container_contract": {
+ "service_name": "campfire",
+ "container_name": "campfire",
+ "image": {
+ "reference": "ghcr.io/awsomefox/campfire:latest",
+ "registry": "ghcr.io",
+ "repository": "awsomefox/campfire",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "ALLOW_INSECURE_HTTP",
+ "example": null,
+ "required": false,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt": "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise."
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/data",
+ "compose_source_example": "./data-0",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 8080,
+ "published_example": 8080,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "{\n \"name\": \"campfire\",\n \"services\": {\n \"campfire\": {\n \"image\": \"ghcr.io/awsomefox/campfire:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080/tcp\"\n ],\n \"volumes\": [\n \"./data-0:/data\"\n ]\n }\n }\n}"
+ },
+ "compose_stack": {
+ "project_name": "campfire",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "campfire",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "campfire",
+ "image": "ghcr.io/awsomefox/campfire:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "ghcr.io/awsomefox/campfire:latest",
+ "restart": "unless-stopped",
+ "ports": [
+ "8080:8080/tcp"
+ ],
+ "volumes": [
+ "./data-0:/data"
+ ]
+ }
+ }
+ ],
+ "top_level": {
+ "name": "campfire"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "campfire-volume-0",
+ "service": "campfire",
+ "container_path": "/data",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "campfire"
+ ],
+ "stop_order": [
+ "campfire"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": []
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Campfire first-run setup",
+ "scheme": "http",
+ "port": 8080,
+ "path": "/auth/setup",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": []
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 1024,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 120
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/readyz",
+ "timeout_seconds": 240,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 4,
+ "verify_tls": false
+ },
+ "volume_owner": {
+ "uid": 1000,
+ "gid": 1000
+ },
+ "volume_preparations": [
+ {
+ "container_path": "/data",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-application-user",
+ "mode": "2775",
+ "only_when_mount_type": "managed-volume"
+ }
+ ]
+ },
+ "catalog": {
+ "replaces_discovered_ids": []
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/apps/chhoto-url.json b/oci/catalog/apps/chhoto-url.json
new file mode 100644
index 00000000..26f8caec
--- /dev/null
+++ b/oci/catalog/apps/chhoto-url.json
@@ -0,0 +1,500 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-chhoto-url",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Chhoto URL",
+ "es_ES": "Chhoto URL"
+ },
+ "tagline": {
+ "en_US": "A lightweight self-hosted URL shortener with SQLite storage.",
+ "es_ES": "Un acortador de enlaces ligero y autoalojado con almacenamiento SQLite."
+ },
+ "description": {
+ "en_US": "Create and manage short links from a small web interface. Chhoto URL stores its link database in SQLite and supports password-protected administration without a separate database service.",
+ "es_ES": "Crea y administra enlaces cortos desde una interfaz web sencilla. Chhoto URL guarda los enlaces en SQLite y protege la administración con contraseña, sin necesitar otra base de datos."
+ },
+ "category": "tools",
+ "category_label": "Tools",
+ "author": "sintan1729",
+ "developer": "sintan1729",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/chhoto-url.webp",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 4567,
+ "path": "/"
+ },
+ "website": "https://github.com/SinTan1729/chhoto-url",
+ "documentation": "https://github.com/SinTan1729/chhoto-url/blob/main/docs/INSTALLATION.md",
+ "repository": "https://github.com/SinTan1729/chhoto-url",
+ "tips": [
+ "The installer generates the required administrator password. Use HTTPS through a reverse proxy when sending the password over a network; upstream notes that it is not encrypted in plain HTTP."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "sintan1729",
+ "repository": "https://github.com/SinTan1729/chhoto-url",
+ "revision": "611ac292c2dc998fac78a3f726afc3c61a344991",
+ "image_repository_url": "https://hub.docker.com/r/sintan1729/chhoto-url",
+ "readme_pushed_at": "",
+ "default_branch": "main",
+ "compose_sha256": "ccfc1092edaa68277222f013a8c42a4f33c01f0e70bfffb78188c33643f28a87",
+ "generated_at": "2026-10-26T08:45:03+00:00",
+ "readme_raw_url": "https://raw.githubusercontent.com/SinTan1729/chhoto-url/main/README.md"
+ },
+ "container_contract": {
+ "service_name": "chhoto-url",
+ "container_name": "chhoto-url",
+ "image": {
+ "reference": "sintan1729/chhoto-url:latest",
+ "registry": "docker.io",
+ "repository": "sintan1729/chhoto-url",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "CHHOTO_PASSWORD",
+ "example": "${GENERATED_CHHOTO_PASSWORD}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "CHHOTO_DB_URL",
+ "example": "/data/urls.sqlite",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt_user": false
+ },
+ {
+ "name": "CHHOTO_SQLITE_USE_WAL_MODE",
+ "example": "True",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt_user": false
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/data",
+ "compose_source_example": "data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 4
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 4567,
+ "published_example": 4567,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "services:\n chhoto-url:\n image: sintan1729/chhoto-url:latest\n container_name: chhoto-url\n restart: unless-stopped\n ports:\n - 4567:4567/tcp\n volumes:\n - ./data:/data\n environment:\n CHHOTO_PASSWORD: ${GENERATED_CHHOTO_PASSWORD}\n CHHOTO_DB_URL: /data/urls.sqlite\n CHHOTO_SQLITE_USE_WAL_MODE: 'True'\n"
+ },
+ "compose_stack": {
+ "project_name": "chhoto-url",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "chhoto-url",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "chhoto-url",
+ "image": "sintan1729/chhoto-url:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "environment": {
+ "CHHOTO_PASSWORD": "${GENERATED_CHHOTO_PASSWORD}",
+ "CHHOTO_DB_URL": "/data/urls.sqlite",
+ "CHHOTO_SQLITE_USE_WAL_MODE": "True"
+ },
+ "image": "sintan1729/chhoto-url:latest",
+ "ports": [
+ "4567:4567/tcp"
+ ],
+ "volumes": [
+ "./data:/data"
+ ],
+ "container_name": "chhoto-url",
+ "restart": "unless-stopped"
+ }
+ }
+ ],
+ "top_level": {
+ "name": "chhoto-url"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "chhoto-url-volume-0",
+ "service": "chhoto-url",
+ "container_path": "/data",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "chhoto-url"
+ ],
+ "stop_order": [
+ "chhoto-url"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "generated-chhoto-password",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "chhoto-url",
+ "environment_variable": "CHHOTO_PASSWORD"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Chhoto URL",
+ "scheme": "http",
+ "port": 4567,
+ "path": "/",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": [
+ {
+ "label": "Chhoto URL administrator password",
+ "type": "configured-or-installer-generated",
+ "username": "Not required (password only)",
+ "password": null,
+ "password_environment": "CHHOTO_PASSWORD",
+ "change_required": false,
+ "source": "proxmenux-installer-generated",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 512,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "volume_preparations": [
+ {
+ "container_path": "/data",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-root",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "generated_sensitive_environment": {
+ "CHHOTO_PASSWORD": {
+ "strategy": "token-hex",
+ "bytes": 24,
+ "prompt": false
+ }
+ },
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 4567,
+ "path": "/",
+ "timeout_seconds": 180,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/apps/ech0.json b/oci/catalog/apps/ech0.json
new file mode 100644
index 00000000..c20f649e
--- /dev/null
+++ b/oci/catalog/apps/ech0.json
@@ -0,0 +1,482 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-ech0",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Ech0",
+ "es_ES": "Ech0"
+ },
+ "tagline": {
+ "en_US": "A personal, self-hosted microblog and timeline.",
+ "es_ES": "Un microblog y una cronología personal que alojas en tu propio servidor."
+ },
+ "description": {
+ "en_US": "Publish short posts, links and media on a personal timeline that you control. Ech0 includes accounts, comments, RSS and local data storage in one lightweight service.",
+ "es_ES": "Publica textos breves, enlaces y contenido multimedia en una cronología propia. Ech0 incluye cuentas, comentarios, RSS y almacenamiento local en un único servicio."
+ },
+ "category": "communication",
+ "category_label": "Communication & Community",
+ "author": "lin-snow",
+ "developer": "lin-snow",
+ "icon": "https://raw.githubusercontent.com/lin-snow/Ech0/main/docs/imgs/logo.svg",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 6277,
+ "path": "/"
+ },
+ "website": "https://www.ech0.app/",
+ "documentation": "https://github.com/lin-snow/Ech0/blob/main/DEPLOYMENT.md",
+ "repository": "https://github.com/lin-snow/Ech0",
+ "tips": [
+ "The first registered account becomes the Owner. Publishing is restricted to privileged accounts by default.",
+ "Keep the generated JWT secret and the /app/data volume persistent; back up the data volume and retain the same secret across restarts."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "lin-snow",
+ "repository": "https://github.com/lin-snow/Ech0",
+ "revision": "f2da207f4ab7bd89053898dd1e18ffb4ded1204d",
+ "image_repository_url": "https://hub.docker.com/r/sn0wl1n/ech0",
+ "readme_pushed_at": "",
+ "default_branch": "main",
+ "compose_sha256": "3ccb02dfdb7cd5198b2181a1a2f78e0b3a7e205efaf6920c1ca1257a4416b0bd",
+ "generated_at": "2026-10-26T09:32:51+00:00",
+ "readme_raw_url": "https://raw.githubusercontent.com/lin-snow/Ech0/main/README.md"
+ },
+ "container_contract": {
+ "service_name": "ech0",
+ "container_name": "ech0",
+ "image": {
+ "reference": "sn0wl1n/ech0:latest",
+ "registry": "docker.io",
+ "repository": "sn0wl1n/ech0",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "JWT_SECRET",
+ "example": "${GENERATED_JWT_SECRET}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/app/data",
+ "compose_source_example": "data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 6277,
+ "published_example": 6277,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "services:\n ech0:\n image: sn0wl1n/ech0:latest\n container_name: ech0\n restart: unless-stopped\n ports:\n - 6277:6277/tcp\n volumes:\n - ./data:/app/data\n environment:\n JWT_SECRET: ${GENERATED_JWT_SECRET}\n"
+ },
+ "compose_stack": {
+ "project_name": "ech0",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "ech0",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "ech0",
+ "image": "sn0wl1n/ech0:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "sn0wl1n/ech0:latest",
+ "ports": [
+ "6277:6277/tcp"
+ ],
+ "volumes": [
+ "./data:/app/data"
+ ],
+ "container_name": "ech0",
+ "restart": "unless-stopped",
+ "environment": {
+ "JWT_SECRET": "${GENERATED_JWT_SECRET}"
+ }
+ }
+ }
+ ],
+ "top_level": {
+ "name": "ech0"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "ech0-volume-0",
+ "service": "ech0",
+ "container_path": "/app/data",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "ech0"
+ ],
+ "stop_order": [
+ "ech0"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "generated-jwt-secret",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "ech0",
+ "environment_variable": "JWT_SECRET"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Ech0",
+ "scheme": "http",
+ "port": 6277,
+ "path": "/",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": [
+ {
+ "label": "First Ech0 account (becomes Owner)",
+ "type": "configured-or-installer-generated",
+ "username": "Created during first-run setup",
+ "password": null,
+ "change_required": false,
+ "source": "upstream-documentation",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 512,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "volume_preparations": [
+ {
+ "container_path": "/app/data",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-root",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "generated_sensitive_environment": {
+ "JWT_SECRET": {
+ "strategy": "token-hex",
+ "bytes": 32,
+ "prompt": false
+ }
+ },
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 6277,
+ "path": "/",
+ "timeout_seconds": 180,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/apps/kopia.json b/oci/catalog/apps/kopia.json
new file mode 100644
index 00000000..89fd5d55
--- /dev/null
+++ b/oci/catalog/apps/kopia.json
@@ -0,0 +1,540 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-kopia",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Kopia",
+ "es_ES": "Kopia"
+ },
+ "tagline": {
+ "en_US": "Create encrypted, deduplicated backups and restore files.",
+ "es_ES": "Crea copias cifradas y deduplicadas y restaura archivos."
+ },
+ "description": {
+ "en_US": "Kopia provides encrypted, deduplicated backups with a web interface for repositories, policies, snapshots and restores. This profile supports browsing and restoring files through the UI; mounting snapshots with FUSE is not enabled.",
+ "es_ES": "Kopia ofrece copias cifradas y deduplicadas, con una interfaz web para repositorios, políticas, snapshots y restauraciones. Este perfil permite explorar y restaurar archivos desde la interfaz; no habilita el montaje de snapshots mediante FUSE."
+ },
+ "category": "backup",
+ "category_label": "Backup & Recovery",
+ "author": "kopia",
+ "developer": "kopia",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kopia.webp",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 51515,
+ "path": "/"
+ },
+ "website": "https://github.com/kopia/kopia",
+ "documentation": "https://kopia.io/docs/installation/",
+ "repository": "https://github.com/kopia/kopia",
+ "tips": [
+ "The installer generates a web UI password and a separate repository encryption password. Keep the Proxmox LXC configuration and /app/config volume in backups; without the repository password, encrypted snapshots cannot be opened.",
+ "On first visit, create a repository in Kopia UI at /repository, then add /data as a read-only source and choose /restore for restores. Choose a host bind for /data and for /repository if you want to back up a NAS or other host directory.",
+ "The recipe keeps the LXC unprivileged and does not configure FUSE snapshot mounting. Use Kopia UI restore workflows rather than mounting snapshots into the host.",
+ "The server uses HTTP on its LXC address. Keep it on a trusted LAN or put it behind an HTTPS reverse proxy before allowing access from untrusted networks.",
+ "This profile uses the web interface to browse and restore files. It does not enable mounting snapshots with FUSE or shared mount propagation.",
+ "Este perfil usa la interfaz web para explorar y restaurar archivos. No habilita el montaje de snapshots con FUSE ni la propagación compartida de montajes."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "kopia",
+ "repository": "https://github.com/kopia/kopia",
+ "revision": "0835005737025dc3e5203df0138c8265686cb4b3",
+ "image_repository_url": "https://hub.docker.com/r/kopia/kopia",
+ "readme_pushed_at": "",
+ "compose_sha256": "e17828b2fb52ceea822dc412d87549dabe2fb0f404ba16b21272e01d8a78b5c8",
+ "generated_at": "2026-10-26T06:50:27+00:00",
+ "readme_raw_url": "https://kopia.io/docs/installation/"
+ },
+ "container_contract": {
+ "service_name": "kopia",
+ "container_name": "kopia",
+ "image": {
+ "reference": "kopia/kopia:latest",
+ "registry": "docker.io",
+ "repository": "kopia/kopia",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "KOPIA_PASSWORD",
+ "example": "${GENERATED_KOPIA_PASSWORD}",
+ "required": true,
+ "sensitive": true,
+ "source": "official-docker-documentation"
+ },
+ {
+ "name": "KOPIA_SERVER_USERNAME",
+ "example": "kopia",
+ "required": true,
+ "sensitive": false,
+ "source": "official-kopia-cli",
+ "prompt_user": false
+ },
+ {
+ "name": "KOPIA_SERVER_PASSWORD",
+ "example": "${GENERATED_KOPIA_SERVER_PASSWORD}",
+ "required": true,
+ "sensitive": true,
+ "source": "official-kopia-cli"
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/app/config",
+ "compose_source_example": "kopia-config",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 2
+ }
+ },
+ {
+ "id": "volume-1",
+ "container_path": "/app/cache",
+ "compose_source_example": "kopia-cache",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 4
+ }
+ },
+ {
+ "id": "volume-2",
+ "container_path": "/app/logs",
+ "compose_source_example": "kopia-logs",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 2
+ }
+ },
+ {
+ "id": "volume-3",
+ "container_path": "/data",
+ "compose_source_example": "./backup-source",
+ "read_only": true,
+ "required": true,
+ "installation_choice": [
+ "host-bind"
+ ],
+ "default": "host-bind",
+ "managed_volume": {
+ "backup": false,
+ "default_size_gb": 1
+ }
+ },
+ {
+ "id": "volume-4",
+ "container_path": "/repository",
+ "compose_source_example": "./kopia-repository",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 16
+ }
+ },
+ {
+ "id": "volume-5",
+ "container_path": "/restore",
+ "compose_source_example": "kopia-restore",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 51515,
+ "published_example": 51515,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "{\n \"name\": \"kopia\",\n \"services\": {\n \"kopia\": {\n \"image\": \"kopia/kopia:latest\",\n \"container_name\": \"kopia\",\n \"hostname\": \"kopia\",\n \"restart\": \"unless-stopped\",\n \"command\": [\n \"server\",\n \"start\",\n \"--async-repo-connect\",\n \"--address=0.0.0.0:51515\",\n \"--server-username=kopia\"\n ],\n \"environment\": {\n \"KOPIA_PASSWORD\": \"${GENERATED_KOPIA_PASSWORD}\",\n \"KOPIA_SERVER_USERNAME\": \"kopia\",\n \"KOPIA_SERVER_PASSWORD\": \"${GENERATED_KOPIA_SERVER_PASSWORD}\"\n },\n \"ports\": [\n \"51515:51515/tcp\"\n ],\n \"volumes\": [\n \"kopia-config:/app/config\",\n \"kopia-cache:/app/cache\",\n \"kopia-logs:/app/logs\",\n \"./backup-source:/data:ro\",\n \"kopia-repository:/repository\",\n \"kopia-restore:/restore\"\n ]\n }\n }\n}\n"
+ },
+ "compose_stack": {
+ "project_name": "kopia",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "kopia",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "kopia",
+ "image": "kopia/kopia:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "kopia/kopia:latest",
+ "container_name": "kopia",
+ "hostname": "kopia",
+ "restart": "unless-stopped",
+ "command": [
+ "server",
+ "start",
+ "--async-repo-connect",
+ "--address=0.0.0.0:51515",
+ "--server-username=kopia",
+ "--insecure"
+ ],
+ "environment": {
+ "KOPIA_PASSWORD": "${GENERATED_KOPIA_PASSWORD}",
+ "KOPIA_SERVER_USERNAME": "kopia",
+ "KOPIA_SERVER_PASSWORD": "${GENERATED_KOPIA_SERVER_PASSWORD}"
+ },
+ "ports": [
+ "51515:51515/tcp"
+ ],
+ "volumes": [
+ "kopia-config:/app/config",
+ "kopia-cache:/app/cache",
+ "kopia-logs:/app/logs",
+ "./backup-source:/data:ro",
+ "kopia-repository:/repository",
+ "kopia-restore:/restore"
+ ]
+ }
+ }
+ ],
+ "top_level": {
+ "name": "kopia"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "kopia-volume-0",
+ "service": "kopia",
+ "container_path": "/app/config",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ },
+ {
+ "id": "kopia-volume-1",
+ "service": "kopia",
+ "container_path": "/app/cache",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ },
+ {
+ "id": "kopia-volume-2",
+ "service": "kopia",
+ "container_path": "/app/logs",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ },
+ {
+ "id": "kopia-volume-3",
+ "service": "kopia",
+ "container_path": "/data",
+ "mode": "host-bind",
+ "user_selectable": false,
+ "backup": false,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ },
+ {
+ "id": "kopia-volume-4",
+ "service": "kopia",
+ "container_path": "/repository",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ },
+ {
+ "id": "kopia-volume-5",
+ "service": "kopia",
+ "container_path": "/restore",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "kopia"
+ ],
+ "stop_order": [
+ "kopia"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "kopia-password",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "kopia",
+ "environment_variable": "KOPIA_PASSWORD"
+ }
+ ]
+ },
+ {
+ "id": "kopia-server-password",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "kopia",
+ "environment_variable": "KOPIA_SERVER_PASSWORD"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Kopia Web UI",
+ "scheme": "http",
+ "port": 51515,
+ "path": "/",
+ "source": "official-docker-documentation"
+ }
+ ],
+ "credentials": [
+ {
+ "label": "Kopia Web UI login",
+ "type": "configured-or-installer-generated",
+ "username": "kopia",
+ "password": null,
+ "password_environment": "KOPIA_SERVER_PASSWORD",
+ "change_required": false,
+ "source": "official-kopia-cli",
+ "retrieval": null
+ },
+ {
+ "label": "Repository encryption password",
+ "type": "configured-or-installer-generated",
+ "username": "Not applicable",
+ "password": null,
+ "password_environment": "KOPIA_PASSWORD",
+ "change_required": false,
+ "source": "official-docker-documentation",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 2048,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 120
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "The Compose definition publishes service ports on its Docker host.",
+ "native_lxc_behavior": "ProxMenux keeps the image service ports and exposes them on this LXC address without Docker port NAT.",
+ "reason": "The application runs in its own Proxmox LXC network namespace.",
+ "behavioral_impact": "Connect to the LXC address and the service port shown in the install summary.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose provides the command that starts the service.",
+ "native_lxc_behavior": "ProxMenux imports the image entrypoint and applies the reviewed command as the native OCI process.",
+ "reason": "The image runs directly as a native OCI LXC process.",
+ "behavioral_impact": "The process starts with the upstream server command.",
+ "validation": "pending-per-application"
+ }
+ ],
+ "installer_profile": {
+ "generated_sensitive_environment": {
+ "KOPIA_PASSWORD": {
+ "strategy": "token-hex",
+ "bytes": 32,
+ "prompt": false
+ },
+ "KOPIA_SERVER_PASSWORD": {
+ "strategy": "token-hex",
+ "bytes": 24,
+ "prompt": true
+ }
+ },
+ "runtime": {
+ "command": [
+ "server",
+ "start",
+ "--async-repo-connect",
+ "--address=0.0.0.0:51515",
+ "--server-username=kopia",
+ "--insecure"
+ ],
+ "hostname": "kopia"
+ },
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 51515,
+ "path": "/",
+ "timeout_seconds": 240,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "command",
+ "container_name",
+ "environment",
+ "hostname",
+ "image",
+ "ports",
+ "restart",
+ "volumes"
+ ],
+ "untranslated_blockers": [],
+ "policy": "The single upstream image runs as a native Proxmox OCI LXC. Only the documented image command, environment, ports and persistent paths are translated."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/apps/metabase.json b/oci/catalog/apps/metabase.json
new file mode 100644
index 00000000..8629d34b
--- /dev/null
+++ b/oci/catalog/apps/metabase.json
@@ -0,0 +1,534 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-metabase",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Metabase",
+ "es_ES": "Metabase"
+ },
+ "tagline": {
+ "en_US": "Build reports and dashboards from your data.",
+ "es_ES": "Crea informes y paneles a partir de tus datos."
+ },
+ "description": {
+ "en_US": "An open-source business intelligence platform for querying data and building dashboards. This single-container profile uses embedded H2 for a home-lab trial or small instance; PostgreSQL is recommended for production.",
+ "es_ES": "Plataforma de inteligencia de negocio de código abierto para consultar datos y crear paneles. Este perfil de un solo contenedor usa H2 integrada para pruebas o instalaciones pequeñas; para producción se recomienda PostgreSQL."
+ },
+ "category": "monitoring",
+ "category_label": "Monitoring & Analytics",
+ "author": "Metabase",
+ "developer": "Metabase",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/metabase.webp",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 3000,
+ "path": "/"
+ },
+ "website": "https://www.metabase.com/",
+ "documentation": "https://www.metabase.com/docs/latest/installation-and-operation/running-metabase-on-docker",
+ "repository": "https://github.com/metabase/metabase",
+ "tips": [
+ "This profile uses the built-in H2 application database for a simple single-container home-lab setup. Metabase recommends PostgreSQL for production; its application database stores users, questions, dashboards and settings.",
+ "On first visit, complete the setup wizard and create the administrator account. Keep the service on a trusted LAN until setup and authentication are complete.",
+ "The H2 database is stored at /metabase-data/metabase.db. Keep the managed volume persistent and back it up before upgrades or migration.",
+ "ProxMenux generates MB_ENCRYPTION_SECRET_KEY at installation. Keep that key with your backups; losing it can prevent Metabase from decrypting saved database connection credentials.",
+ "The image follows the v0.64 LTS release track. The installer gives it 2 GiB RAM and caps the Java heap at 1 GiB, leaving memory for the rest of the JVM.",
+ "The embedded H2 application database is suitable for a trial or small instance. Metabase recommends PostgreSQL for production; this profile does not add a separate database service.",
+ "La base de datos H2 integrada sirve para pruebas o instalaciones pequeñas. Metabase recomienda PostgreSQL para producción; este perfil no añade una base de datos aparte."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "metabase",
+ "repository": "https://github.com/metabase/metabase",
+ "revision": "9fe55abf5c14d7b4a585be9736affcdd0890be36",
+ "image_repository_url": "https://hub.docker.com/r/metabase/metabase",
+ "readme_pushed_at": "",
+ "compose_sha256": "eb8044f4c7c178e640b24d7cd0e6ce2edf51aafcf4f30ee112f5f1e37b4a2dd3",
+ "generated_at": "2026-10-26T07:19:57+00:00",
+ "readme_raw_url": "https://www.metabase.com/docs/latest/installation-and-operation/running-metabase-on-docker"
+ },
+ "container_contract": {
+ "service_name": "metabase",
+ "container_name": "metabase",
+ "image": {
+ "reference": "metabase/metabase:v0.64-lts",
+ "registry": "docker.io",
+ "repository": "metabase/metabase",
+ "tag": "v0.64-lts",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "MB_DB_TYPE",
+ "example": "h2",
+ "required": true,
+ "sensitive": false,
+ "source": "docker-compose"
+ },
+ {
+ "name": "MB_DB_FILE",
+ "example": "/metabase-data/metabase.db",
+ "required": true,
+ "sensitive": false,
+ "source": "docker-compose"
+ },
+ {
+ "name": "MB_ENCRYPTION_SECRET_KEY",
+ "example": "${GENERATED_MB_ENCRYPTION_SECRET_KEY}",
+ "required": true,
+ "sensitive": true,
+ "source": "docker-compose"
+ },
+ {
+ "name": "MUID",
+ "example": "2000",
+ "required": true,
+ "sensitive": false,
+ "source": "docker-compose"
+ },
+ {
+ "name": "MGID",
+ "example": "2000",
+ "required": true,
+ "sensitive": false,
+ "source": "docker-compose"
+ },
+ {
+ "name": "JAVA_OPTS",
+ "example": "-Xmx1g",
+ "required": true,
+ "sensitive": false,
+ "source": "docker-compose"
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/metabase-data",
+ "compose_source_example": "metabase-data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 3000,
+ "published_example": 3000,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "name: metabase\nservices:\n metabase:\n image: metabase/metabase:v0.64-lts\n restart: unless-stopped\n ports:\n - 3000:3000/tcp\n volumes:\n - metabase-data:/metabase-data\n environment:\n MB_DB_TYPE: h2\n MB_DB_FILE: /metabase-data/metabase.db\n MB_ENCRYPTION_SECRET_KEY: ${GENERATED_MB_ENCRYPTION_SECRET_KEY}\n MUID: '2000'\n MGID: '2000'\n JAVA_OPTS: -Xmx1g\n healthcheck:\n test:\n - CMD-SHELL\n - curl --fail -I http://localhost:3000/api/health || exit 1\n interval: 15s\n timeout: 5s\n retries: 5\n start_period: 180s\nvolumes:\n metabase-data: {}\n"
+ },
+ "compose_stack": {
+ "project_name": "metabase",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "metabase",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "metabase",
+ "image": "metabase/metabase:v0.64-lts",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "metabase/metabase:v0.64-lts",
+ "restart": "unless-stopped",
+ "ports": [
+ "3000:3000/tcp"
+ ],
+ "volumes": [
+ "metabase-data:/metabase-data"
+ ],
+ "environment": {
+ "MB_DB_TYPE": "h2",
+ "MB_DB_FILE": "/metabase-data/metabase.db",
+ "MB_ENCRYPTION_SECRET_KEY": "${GENERATED_MB_ENCRYPTION_SECRET_KEY}",
+ "MUID": "2000",
+ "MGID": "2000",
+ "JAVA_OPTS": "-Xmx1g"
+ },
+ "healthcheck": {
+ "test": [
+ "CMD-SHELL",
+ "curl --fail -I http://localhost:3000/api/health || exit 1"
+ ],
+ "interval": "15s",
+ "timeout": "5s",
+ "retries": 5,
+ "start_period": "180s"
+ }
+ }
+ }
+ ],
+ "top_level": {
+ "name": "metabase",
+ "volumes": {
+ "metabase-data": {}
+ }
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "metabase-volume-0",
+ "service": "metabase",
+ "container_path": "/metabase-data",
+ "mode": "managed-volume",
+ "user_selectable": false,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "metabase"
+ ],
+ "stop_order": [
+ "metabase"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "mb-encryption-secret-key",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "metabase",
+ "environment_variable": "MB_ENCRYPTION_SECRET_KEY"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Web UI",
+ "scheme": "http",
+ "port": 3000,
+ "path": "/",
+ "source": "compose-metadata"
+ }
+ ],
+ "credentials": []
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 2048,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "installer_profile": {
+ "startup_healthcheck": {
+ "type": "http",
+ "scheme": "http",
+ "port": 3000,
+ "path": "/api/health",
+ "timeout_seconds": 300,
+ "request_timeout_seconds": 5,
+ "stability_seconds": 0,
+ "verify_tls": false,
+ "required": true,
+ "source": "compose-healthcheck"
+ },
+ "volume_preparations": [
+ {
+ "container_path": "/metabase-data",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-application-user",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "volume_owner": {
+ "uid": 2000,
+ "gid": 2000
+ },
+ "generated_sensitive_environment": {
+ "MB_ENCRYPTION_SECRET_KEY": {
+ "strategy": "token-hex",
+ "bytes": 32,
+ "prompt": false
+ }
+ }
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ]
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/apps/openhabittracker.json b/oci/catalog/apps/openhabittracker.json
new file mode 100644
index 00000000..4963b840
--- /dev/null
+++ b/oci/catalog/apps/openhabittracker.json
@@ -0,0 +1,533 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-openhabittracker",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "OpenHabitTracker",
+ "es_ES": "OpenHabitTracker"
+ },
+ "tagline": {
+ "en_US": "A self-hosted tracker for habits, tasks, notes and goals.",
+ "es_ES": "Seguimiento autoalojado de hábitos, tareas, notas y objetivos."
+ },
+ "description": {
+ "en_US": "The official image includes the Blazor Server application for one user. It persists application data and backups in /app/.OpenHabitTracker.",
+ "es_ES": "La imagen oficial incluye la aplicación Blazor Server para una persona. Guarda los datos y las copias de seguridad en /app/.OpenHabitTracker."
+ },
+ "category": "productivity",
+ "category_label": "Productivity & Workflows",
+ "author": "Jinjinov",
+ "developer": "Jinjinov",
+ "icon": "https://cdn.jsdelivr.net/gh/Jinjinov/OpenHabitTracker@main/OpenHabitTracker.Web/icons/icon-512.png",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/login"
+ },
+ "website": "https://github.com/Jinjinov/OpenHabitTracker",
+ "documentation": "https://github.com/Jinjinov/OpenHabitTracker/blob/main/README.md",
+ "repository": "https://github.com/Jinjinov/OpenHabitTracker",
+ "tips": [
+ "The installer sets the initial username, email and password. Sign in at /login after startup."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "Jinjinov",
+ "repository": "https://github.com/Jinjinov/OpenHabitTracker",
+ "revision": "a285bf78c04ecebf3b3acc701f1776d4da326905",
+ "image_repository_url": "https://hub.docker.com/r/jinjinov/openhabittracker",
+ "readme_pushed_at": "",
+ "default_branch": "main",
+ "compose_sha256": "1405f9aea98cdf7cc414b9c00a13a1b5086cba2de51f3ba6d1d607e48c94a5d8",
+ "generated_at": "2026-10-26T08:45:03+00:00",
+ "readme_raw_url": "https://raw.githubusercontent.com/Jinjinov/OpenHabitTracker/main/README.md"
+ },
+ "container_contract": {
+ "service_name": "openhabittracker",
+ "container_name": "openhabittracker",
+ "image": {
+ "reference": "jinjinov/openhabittracker:latest",
+ "registry": "docker.io",
+ "repository": "jinjinov/openhabittracker",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "AppSettings__UserName",
+ "example": "",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt": "OpenHabitTracker initial username"
+ },
+ {
+ "name": "AppSettings__Email",
+ "example": "",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt": "Email address for the OpenHabitTracker account"
+ },
+ {
+ "name": "AppSettings__Password",
+ "example": "${GENERATED_APPSETTINGS_PASSWORD}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "AppSettings__JwtSecret",
+ "example": "${GENERATED_APPSETTINGS_JWT_SECRET}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "TZ",
+ "example": "Europe/Madrid",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt_user": false
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/app/.OpenHabitTracker",
+ "compose_source_example": "data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 8080,
+ "published_example": 8080,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "services:\n openhabittracker:\n image: jinjinov/openhabittracker:latest\n container_name: openhabittracker\n restart: unless-stopped\n ports:\n - 8080:8080/tcp\n volumes:\n - ./data:/app/.OpenHabitTracker\n environment:\n AppSettings__UserName: ${AppSettings__UserName}\n AppSettings__Email: ${AppSettings__Email}\n AppSettings__Password: ${GENERATED_APPSETTINGS_PASSWORD}\n AppSettings__JwtSecret: ${GENERATED_APPSETTINGS_JWT_SECRET}\n TZ: Europe/Madrid\n"
+ },
+ "compose_stack": {
+ "project_name": "openhabittracker",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "openhabittracker",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "openhabittracker",
+ "image": "jinjinov/openhabittracker:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "environment": {
+ "AppSettings__UserName": "${AppSettings__UserName}",
+ "AppSettings__Email": "${AppSettings__Email}",
+ "AppSettings__Password": "${GENERATED_APPSETTINGS_PASSWORD}",
+ "AppSettings__JwtSecret": "${GENERATED_APPSETTINGS_JWT_SECRET}",
+ "TZ": "Europe/Madrid"
+ },
+ "image": "jinjinov/openhabittracker:latest",
+ "ports": [
+ "8080:8080/tcp"
+ ],
+ "volumes": [
+ "./data:/app/.OpenHabitTracker"
+ ],
+ "container_name": "openhabittracker",
+ "restart": "unless-stopped"
+ }
+ }
+ ],
+ "top_level": {
+ "name": "openhabittracker"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "openhabittracker-volume-0",
+ "service": "openhabittracker",
+ "container_path": "/app/.OpenHabitTracker",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "openhabittracker"
+ ],
+ "stop_order": [
+ "openhabittracker"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "generated-appsettings-password",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "openhabittracker",
+ "environment_variable": "AppSettings__Password"
+ }
+ ]
+ },
+ {
+ "id": "generated-appsettings-jwtsecret",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "openhabittracker",
+ "environment_variable": "AppSettings__JwtSecret"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "OpenHabitTracker",
+ "scheme": "http",
+ "port": 8080,
+ "path": "/login",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": [
+ {
+ "label": "OpenHabitTracker account",
+ "type": "configured-or-installer-generated",
+ "username": "Configured username",
+ "username_environment": "AppSettings__UserName",
+ "password": null,
+ "password_environment": "AppSettings__Password",
+ "change_required": false,
+ "source": "proxmenux-installer-generated",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 512,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "volume_preparations": [
+ {
+ "container_path": "/app/.OpenHabitTracker",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-root",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "generated_sensitive_environment": {
+ "AppSettings__Password": {
+ "strategy": "token-hex",
+ "bytes": 24,
+ "prompt": false
+ },
+ "AppSettings__JwtSecret": {
+ "strategy": "token-hex",
+ "bytes": 32,
+ "prompt": false
+ }
+ },
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/login",
+ "timeout_seconds": 180,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/apps/restic-rest-server.json b/oci/catalog/apps/restic-rest-server.json
new file mode 100644
index 00000000..4ebc9455
--- /dev/null
+++ b/oci/catalog/apps/restic-rest-server.json
@@ -0,0 +1,315 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-restic-rest-server",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Restic REST Server",
+ "es_ES": "Restic REST Server"
+ },
+ "tagline": {
+ "en_US": "A REST backend for restic backups; it has no web interface.",
+ "es_ES": "Un backend REST para copias de Restic, sin interfaz web."
+ },
+ "description": {
+ "en_US": "The official rest-server image provides an authenticated HTTP REST backend for restic clients, with persistent repositories and bcrypt-protected user accounts. It exposes an API, not a browser-based application.",
+ "es_ES": "La imagen oficial rest-server ofrece un backend HTTP REST autenticado para clientes Restic, con repositorios persistentes y cuentas protegidas con bcrypt. Expone una API, no una aplicación web."
+ },
+ "category": "backup",
+ "category_label": "Backup & Recovery",
+ "author": "restic",
+ "developer": "restic",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/restic.webp",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": null,
+ "path": "/"
+ },
+ "website": "https://github.com/restic/rest-server",
+ "documentation": "https://github.com/restic/rest-server",
+ "repository": "https://github.com/restic/rest-server",
+ "tips": [
+ "Authentication stays enabled. Before connecting any backup client, open the container console and run create_user ; enter the password at the hidden prompt. Do not pass the password as a command-line argument. The image stores the bcrypt hash in /data/.htpasswd.",
+ "The API has no web interface. Use a restic client with a repository URL that includes the username-specific path, for example rest:http://USER@HOST:8000/USER when private repositories are enabled.",
+ "The upstream default uses HTTP, which sends Basic Authentication credentials in clear text. Use a trusted LAN or route clients through an HTTPS reverse proxy before remote access.",
+ "With --private-repos enabled, each user can access only a matching repository subdirectory. Keep /data persistent; it contains both backups and the authentication file.",
+ "The upstream endpoint uses HTTP. Use an HTTPS reverse proxy for remote clients; the example API URL shown in this entry is HTTP.",
+ "El endpoint original usa HTTP. Para clientes remotos, coloca un proxy HTTPS delante; la URL de ejemplo de esta ficha usa HTTP."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "restic",
+ "repository": "https://github.com/restic/rest-server",
+ "revision": "cfcf3fea23edb404c860e8708e39306717fe3e85",
+ "image_repository_url": "https://hub.docker.com/r/restic/rest-server",
+ "readme_pushed_at": "",
+ "compose_sha256": "f346e67428e1c6e8e265d607401fe5d5289b5c7ad478170abb4984fa8dcb01ad",
+ "generated_at": "2026-10-26T06:50:27+00:00",
+ "readme_raw_url": "https://github.com/restic/rest-server"
+ },
+ "container_contract": {
+ "service_name": "restic-rest-server",
+ "container_name": "restic-rest-server",
+ "image": {
+ "reference": "restic/rest-server:latest",
+ "registry": "docker.io",
+ "repository": "restic/rest-server",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "OPTIONS",
+ "example": "--private-repos",
+ "required": true,
+ "sensitive": false,
+ "source": "official-rest-server-documentation",
+ "prompt_user": false
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/data",
+ "compose_source_example": "restic-data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 16
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 8000,
+ "published_example": 8000,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "{\n \"name\": \"restic-rest-server\",\n \"services\": {\n \"restic-rest-server\": {\n \"image\": \"restic/rest-server:latest\",\n \"container_name\": \"restic-rest-server\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8000:8000/tcp\"\n ],\n \"volumes\": [\n \"restic-data:/data\"\n ],\n \"environment\": {\n \"OPTIONS\": \"--private-repos\"\n }\n }\n },\n \"volumes\": {\n \"restic-data\": {}\n }\n}\n"
+ },
+ "compose_stack": {
+ "project_name": "restic-rest-server",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "restic-rest-server",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "restic-rest-server",
+ "image": "restic/rest-server:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "restic/rest-server:latest",
+ "container_name": "restic-rest-server",
+ "restart": "unless-stopped",
+ "ports": [
+ "8000:8000/tcp"
+ ],
+ "volumes": [
+ "restic-data:/data"
+ ],
+ "environment": {
+ "OPTIONS": "--private-repos"
+ }
+ }
+ }
+ ],
+ "top_level": {
+ "name": "restic-rest-server",
+ "volumes": {
+ "restic-data": {}
+ }
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "restic-rest-server-volume-0",
+ "service": "restic-rest-server",
+ "container_path": "/data",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "restic-rest-server"
+ ],
+ "stop_order": [
+ "restic-rest-server"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": []
+ }
+ },
+ "first_run": {
+ "endpoints": [],
+ "credentials": [
+ {
+ "label": "REST API Basic Authentication user",
+ "username": "Create with create_user in the container console",
+ "password": null,
+ "change_required": false,
+ "source": "official-rest-server-documentation",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 1,
+ "memory_mb": 512,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 120
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "The Compose definition publishes service ports on its Docker host.",
+ "native_lxc_behavior": "ProxMenux keeps the image service ports and exposes them on this LXC address without Docker port NAT.",
+ "reason": "The application runs in its own Proxmox LXC network namespace.",
+ "behavioral_impact": "Connect to the LXC address and the service port shown in the install summary.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose provides the command that starts the service.",
+ "native_lxc_behavior": "ProxMenux imports the image entrypoint and applies the reviewed command as the native OCI process.",
+ "reason": "The image runs directly as a native OCI LXC process.",
+ "behavioral_impact": "The process starts with the upstream server command.",
+ "validation": "pending-per-application"
+ }
+ ],
+ "installer_profile": {
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 8000,
+ "path": "/",
+ "timeout_seconds": 120,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "command",
+ "container_name",
+ "environment",
+ "hostname",
+ "image",
+ "ports",
+ "restart",
+ "volumes"
+ ],
+ "untranslated_blockers": [],
+ "policy": "The single upstream image runs as a native Proxmox OCI LXC. Only the documented image command, environment, ports and persistent paths are translated."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/apps/watcharr.json b/oci/catalog/apps/watcharr.json
new file mode 100644
index 00000000..a72d08ff
--- /dev/null
+++ b/oci/catalog/apps/watcharr.json
@@ -0,0 +1,453 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-watcharr",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Watcharr",
+ "es_ES": "Watcharr"
+ },
+ "tagline": {
+ "en_US": "Track movies, television, anime and games you want to enjoy.",
+ "es_ES": "Lleva un registro de películas, series, anime y videojuegos."
+ },
+ "description": {
+ "en_US": "Track movies, television shows, anime and games you want to watch or play. The single container stores its database and cache in /data.",
+ "es_ES": "Haz seguimiento de películas, series, anime y juegos que quieres ver o jugar. El contenedor guarda la base de datos y la caché en /data."
+ },
+ "category": "media",
+ "category_label": "Media & Streaming",
+ "author": "sbondCo",
+ "developer": "sbondCo",
+ "icon": "https://raw.githubusercontent.com/sbondCo/Watcharr/dev/doc/static/img/logo-col.png",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 3080,
+ "path": "/"
+ },
+ "website": "https://watcharr.app/",
+ "documentation": "https://watcharr.app/docs/installation/docker-compose",
+ "repository": "https://github.com/sbondCo/Watcharr",
+ "tips": [
+ "The first account created becomes the administrator. Keep access limited to your trusted network until initial setup is complete."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "sbondCo",
+ "repository": "https://github.com/sbondCo/Watcharr",
+ "revision": "aa2a765788a4d0f6eb607e7f90acb0d89d62fcd7",
+ "image_repository_url": "https://github.com/orgs/sbondco/packages/container/package/watcharr",
+ "readme_pushed_at": "",
+ "default_branch": "dev",
+ "compose_sha256": "8c4aa4fc5f19e33730685e75e89a7d6a79253bc3ea7ebd6ea6e27e0981fe5f54",
+ "generated_at": "2026-10-26T08:45:03+00:00",
+ "readme_raw_url": "https://raw.githubusercontent.com/sbondCo/Watcharr/dev/README.md"
+ },
+ "container_contract": {
+ "service_name": "watcharr",
+ "container_name": "watcharr",
+ "image": {
+ "reference": "ghcr.io/sbondco/watcharr:latest",
+ "registry": "ghcr.io",
+ "repository": "ghcr.io/sbondco/watcharr",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/data",
+ "compose_source_example": "data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 3080,
+ "published_example": 3080,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "services:\n watcharr:\n image: ghcr.io/sbondco/watcharr:latest\n container_name: watcharr\n restart: unless-stopped\n ports:\n - 3080:3080/tcp\n volumes:\n - ./data:/data\n environment: {}\n"
+ },
+ "compose_stack": {
+ "project_name": "watcharr",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "watcharr",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "watcharr",
+ "image": "ghcr.io/sbondco/watcharr:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "ghcr.io/sbondco/watcharr:latest",
+ "ports": [
+ "3080:3080/tcp"
+ ],
+ "volumes": [
+ "./data:/data"
+ ],
+ "container_name": "watcharr",
+ "restart": "unless-stopped",
+ "environment": {}
+ }
+ }
+ ],
+ "top_level": {
+ "name": "watcharr"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "watcharr-volume-0",
+ "service": "watcharr",
+ "container_path": "/data",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "watcharr"
+ ],
+ "stop_order": [
+ "watcharr"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": []
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Watcharr",
+ "scheme": "http",
+ "port": 3080,
+ "path": "/",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": [
+ {
+ "label": "First Watcharr account (becomes administrator)",
+ "type": "configured-or-installer-generated",
+ "username": "Created during first-run setup",
+ "password": null,
+ "change_required": false,
+ "source": "upstream-documentation",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 512,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "volume_preparations": [
+ {
+ "container_path": "/data",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-root",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 3080,
+ "path": "/",
+ "timeout_seconds": 180,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/categories.json b/oci/catalog/categories.json
index fe44f55b..819818aa 100644
--- a/oci/catalog/categories.json
+++ b/oci/catalog/categories.json
@@ -420,6 +420,16 @@
"zipkin": "monitoring",
"znc": "communication",
"zoraxy": "web",
- "zotero": "documents"
+ "zotero": "documents",
+ "campfire": "gaming",
+ "chhoto-url": "tools",
+ "openhabittracker": "productivity",
+ "watcharr": "media",
+ "ech0": "communication",
+ "activepieces": "automation",
+ "bytebase": "databases",
+ "metabase": "monitoring",
+ "kopia": "backup",
+ "restic-rest-server": "backup"
}
}
diff --git a/oci/catalog/curated/activepieces.json b/oci/catalog/curated/activepieces.json
new file mode 100644
index 00000000..a1c694b2
--- /dev/null
+++ b/oci/catalog/curated/activepieces.json
@@ -0,0 +1,524 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-activepieces",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Activepieces",
+ "es_ES": "Activepieces"
+ },
+ "tagline": {
+ "en_US": "Build visual automations for personal and hobby use.",
+ "es_ES": "Crea automatizaciones visuales para uso personal."
+ },
+ "description": {
+ "en_US": "Build and run automation flows from a self-hosted visual interface. This single-container profile uses Activepieces’ documented personal mode with embedded storage and an in-memory queue.",
+ "es_ES": "Diseña y ejecuta flujos de automatización desde una interfaz visual autoalojada. Este perfil de un solo contenedor usa el modo personal documentado de Activepieces, con almacenamiento integrado y cola en memoria."
+ },
+ "category": "automation",
+ "category_label": "Automation & Scheduling",
+ "author": "Activepieces",
+ "developer": "Activepieces",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/activepieces.webp",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 80,
+ "path": "/"
+ },
+ "website": "https://www.activepieces.com/",
+ "documentation": "https://www.activepieces.com/docs/install/options/docker",
+ "repository": "https://github.com/activepieces/activepieces",
+ "tips": [
+ "The first account you create becomes the administrator; Activepieces has no default username or password.",
+ "Use the base URL entered during setup. Webhooks and app triggers need it to be reachable by third parties.",
+ "This single-container mode uses embedded PGLite and an in-memory queue for personal use on one machine. It is not the production or multi-instance setup."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "activepieces",
+ "repository": "https://github.com/activepieces/activepieces",
+ "revision": "bc26934b0a08842ec4fd24d83ecd08907cbaec1e4d7a985d30138e4f67ea6855",
+ "image_repository_url": "https://github.com/activepieces/activepieces/pkgs/container/activepieces",
+ "readme_pushed_at": "",
+ "compose_sha256": "bc26934b0a08842ec4fd24d83ecd08907cbaec1e4d7a985d30138e4f67ea6855",
+ "generated_at": "2026-10-26T10:22:17+00:00",
+ "readme_raw_url": "https://raw.githubusercontent.com/activepieces/activepieces/main/README.md"
+ },
+ "container_contract": {
+ "service_name": "activepieces",
+ "container_name": "activepieces",
+ "image": {
+ "reference": "ghcr.io/activepieces/activepieces:latest",
+ "registry": "ghcr.io",
+ "repository": "ghcr.io/activepieces/activepieces",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "AP_DB_TYPE",
+ "example": "PGLITE",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "AP_REDIS_TYPE",
+ "example": "MEMORY",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "AP_FRONTEND_URL",
+ "example": "",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt_user": true,
+ "prompt": "Enter the base URL clients and webhook providers will use to reach this Activepieces instance."
+ },
+ {
+ "name": "AP_ENCRYPTION_KEY",
+ "example": "${GENERATED_AP_ENCRYPTION_KEY}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "AP_JWT_SECRET",
+ "example": "${GENERATED_AP_JWT_SECRET}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/root/.activepieces",
+ "compose_source_example": "./activepieces-data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 80,
+ "published_example": 80,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "name: activepieces\nservices:\n activepieces:\n environment:\n AP_DB_TYPE: PGLITE\n AP_ENCRYPTION_KEY: ${GENERATED_GENERATED_AP_ENCRYPTION_KEY}\n AP_FRONTEND_URL: ''\n AP_JWT_SECRET: ${GENERATED_GENERATED_AP_JWT_SECRET}\n AP_REDIS_TYPE: MEMORY\n image: ghcr.io/activepieces/activepieces:latest\n ports:\n - 80:80\n volumes:\n - ./.activepieces:/root/.activepieces\n"
+ },
+ "compose_stack": {
+ "project_name": "activepieces",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "activepieces",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "activepieces",
+ "image": "ghcr.io/activepieces/activepieces:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "environment": {
+ "AP_DB_TYPE": "PGLITE",
+ "AP_ENCRYPTION_KEY": "${GENERATED_GENERATED_AP_ENCRYPTION_KEY}",
+ "AP_FRONTEND_URL": "",
+ "AP_JWT_SECRET": "${GENERATED_GENERATED_AP_JWT_SECRET}",
+ "AP_REDIS_TYPE": "MEMORY"
+ },
+ "image": "ghcr.io/activepieces/activepieces:latest",
+ "ports": [
+ "80:80"
+ ],
+ "volumes": [
+ "./.activepieces:/root/.activepieces"
+ ]
+ }
+ }
+ ],
+ "top_level": {
+ "name": "activepieces"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "activepieces-volume-0",
+ "service": "activepieces",
+ "container_path": "/root/.activepieces",
+ "mode": "managed-volume",
+ "user_selectable": false,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "activepieces"
+ ],
+ "stop_order": [
+ "activepieces"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "generated-ap-encryption-key",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "activepieces",
+ "environment_variable": "AP_ENCRYPTION_KEY"
+ }
+ ]
+ },
+ {
+ "id": "generated-ap-jwt-secret",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "activepieces",
+ "environment_variable": "AP_JWT_SECRET"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Activepieces",
+ "scheme": "http",
+ "port": 80,
+ "path": "/",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": []
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 2048,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "volume_preparations": [
+ {
+ "container_path": "/root/.activepieces",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-application-user",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "generated_sensitive_environment": {
+ "AP_ENCRYPTION_KEY": {
+ "strategy": "token-hex",
+ "bytes": 16
+ },
+ "AP_JWT_SECRET": {
+ "strategy": "token-hex",
+ "bytes": 32
+ }
+ },
+ "minimum_secret_length": {
+ "AP_ENCRYPTION_KEY": 32,
+ "AP_JWT_SECRET": 32
+ },
+ "volume_owner": {
+ "uid": 0,
+ "gid": 0
+ },
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 80,
+ "path": "/api/v1/health",
+ "timeout_seconds": 300,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 5,
+ "verify_tls": false
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/curated/bytebase.json b/oci/catalog/curated/bytebase.json
new file mode 100644
index 00000000..7a9ee724
--- /dev/null
+++ b/oci/catalog/curated/bytebase.json
@@ -0,0 +1,445 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-bytebase",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Bytebase",
+ "es_ES": "Bytebase"
+ },
+ "tagline": {
+ "en_US": "Manage database changes, access and workflows.",
+ "es_ES": "Administra cambios, accesos y flujos de trabajo de bases de datos."
+ },
+ "description": {
+ "en_US": "Manage database changes, access and workflows through a browser-based workspace. The image contains its own database, so this profile does not require a second service.",
+ "es_ES": "Gestiona cambios, accesos y flujos de bases de datos desde un espacio de trabajo web. La imagen incluye su propia base de datos, por lo que este perfil no necesita otro servicio."
+ },
+ "category": "databases",
+ "category_label": "Databases",
+ "author": "Bytebase",
+ "developer": "Bytebase",
+ "icon": "https://cdn.jsdelivr.net/gh/bytebase/bytebase@main/frontend/src/assets/logo-icon.svg",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/"
+ },
+ "website": "https://www.bytebase.com/",
+ "documentation": "https://docs.bytebase.com/get-started/self-host/deploy-with-docker",
+ "repository": "https://github.com/bytebase/bytebase",
+ "tips": [
+ "On first visit, complete the web setup wizard to create the initial administrator and workspace; the image has no default login credentials.",
+ "To use Bytebase for database changes, connect the database instances you want to manage from its web interface.",
+ "En la primera visita, completa el asistente web para crear el administrador y el espacio de trabajo inicial; la imagen no tiene credenciales de acceso predeterminadas.",
+ "Para gestionar cambios de bases de datos, conecta desde la interfaz las instancias que quieras administrar."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "Bytebase",
+ "repository": "https://github.com/bytebase/bytebase",
+ "revision": "e77473a25aa12a669dc15bc33d37c276d7478b47",
+ "image_repository_url": "https://hub.docker.com/r/bytebase/bytebase",
+ "readme_pushed_at": "",
+ "compose_sha256": "b3756c0ae20c2a82b17f7cd04f116c4aef1b4a49d6ccf651bd865d74ab865e22",
+ "generated_at": "2026-10-26T09:44:16+00:00",
+ "readme_raw_url": "https://github.com/bytebase/bytebase",
+ "default_branch": "main"
+ },
+ "container_contract": {
+ "service_name": "bytebase",
+ "container_name": "bytebase",
+ "image": {
+ "reference": "bytebase/bytebase:latest",
+ "registry": "docker.io",
+ "repository": "bytebase/bytebase",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install"
+ },
+ "environment": [],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/var/opt/bytebase",
+ "compose_source_example": "bytebase-data-0",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 16
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 8080,
+ "published_example": 8080,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "{\n \"name\": \"bytebase\",\n \"services\": {\n \"bytebase\": {\n \"image\": \"bytebase/bytebase:latest\",\n \"restart\": \"unless-stopped\",\n \"command\": [\n \"--port\",\n \"8080\",\n \"--data\",\n \"/var/opt/bytebase\"\n ],\n \"ports\": [\n \"8080:8080/tcp\"\n ],\n \"volumes\": [\n \"bytebase-data-0:/var/opt/bytebase\"\n ]\n }\n },\n \"volumes\": {\n \"bytebase-data-0\": {}\n }\n}"
+ },
+ "compose_stack": {
+ "project_name": "bytebase",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "bytebase",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "bytebase",
+ "image": "bytebase/bytebase:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "bytebase/bytebase:latest",
+ "restart": "unless-stopped",
+ "command": [
+ "--port",
+ "8080",
+ "--data",
+ "/var/opt/bytebase"
+ ],
+ "ports": [
+ "8080:8080/tcp"
+ ],
+ "volumes": [
+ "bytebase-data-0:/var/opt/bytebase"
+ ]
+ }
+ }
+ ],
+ "top_level": {
+ "name": "bytebase",
+ "volumes": {
+ "bytebase-data-0": {}
+ }
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "bytebase-volume-0",
+ "service": "bytebase",
+ "container_path": "/var/opt/bytebase",
+ "mode": "managed-volume",
+ "user_selectable": false,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "bytebase"
+ ],
+ "stop_order": [
+ "bytebase"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": []
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Web UI",
+ "scheme": "http",
+ "port": 8080,
+ "path": "/",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": []
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 2048,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 60
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/healthz",
+ "timeout_seconds": 180,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false
+ },
+ "generated_sensitive_environment": {}
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "pending",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/curated/campfire.json b/oci/catalog/curated/campfire.json
new file mode 100644
index 00000000..6a93cb3f
--- /dev/null
+++ b/oci/catalog/curated/campfire.json
@@ -0,0 +1,457 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-campfire",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Campfire",
+ "es_ES": "Campfire"
+ },
+ "tagline": {
+ "en_US": "A self-hosted app for organizing tabletop role-playing campaigns.",
+ "es_ES": "Aplicación autoalojada para organizar partidas de rol de mesa."
+ },
+ "description": {
+ "en_US": "A self-hosted web application for organizing tabletop role-playing games.",
+ "es_ES": "Una aplicación web autoalojada para organizar campañas y sesiones de juegos de rol."
+ },
+ "category": "gaming",
+ "category_label": "Gaming & Leisure",
+ "author": "AwsomeFox",
+ "developer": "AwsomeFox",
+ "icon": "https://raw.githubusercontent.com/AwsomeFox/campfire/main/apps/web/public/pwa-512x512.png",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/auth/setup"
+ },
+ "website": "https://github.com/AwsomeFox/campfire",
+ "documentation": "https://github.com/AwsomeFox/campfire#readme",
+ "repository": "https://github.com/AwsomeFox/campfire",
+ "tips": [
+ "Create the first account at /auth/setup after startup.",
+ "For direct access over plain HTTP on a trusted LAN, set ALLOW_INSECURE_HTTP=1 in advanced settings. Leave it unset when using HTTPS."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "AwsomeFox",
+ "repository": "https://github.com/AwsomeFox/campfire",
+ "revision": "f0dd871f5d2ab0b949db8c8a15a90ec56d40d3dc",
+ "image_repository_url": "https://github.com/AwsomeFox/campfire/pkgs/container/campfire",
+ "readme_pushed_at": "",
+ "compose_sha256": "0cb682cac1fd76326b551b057b28256280b7d7a982f6f418526b39bf75f5da45",
+ "generated_at": "2026-10-26T06:55:28+00:00",
+ "readme_raw_url": "https://github.com/AwsomeFox/campfire#readme"
+ },
+ "container_contract": {
+ "service_name": "campfire",
+ "container_name": "campfire",
+ "image": {
+ "reference": "ghcr.io/awsomefox/campfire:latest",
+ "registry": "ghcr.io",
+ "repository": "awsomefox/campfire",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-selected-tag-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "ALLOW_INSECURE_HTTP",
+ "example": null,
+ "required": false,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt": "Set to 1 only for a private HTTP-only LAN; use HTTPS otherwise."
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/data",
+ "compose_source_example": "./data-0",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 8080,
+ "published_example": 8080,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "{\n \"name\": \"campfire\",\n \"services\": {\n \"campfire\": {\n \"image\": \"ghcr.io/awsomefox/campfire:latest\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8080:8080/tcp\"\n ],\n \"volumes\": [\n \"./data-0:/data\"\n ]\n }\n }\n}"
+ },
+ "compose_stack": {
+ "project_name": "campfire",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "campfire",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "campfire",
+ "image": "ghcr.io/awsomefox/campfire:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "ghcr.io/awsomefox/campfire:latest",
+ "restart": "unless-stopped",
+ "ports": [
+ "8080:8080/tcp"
+ ],
+ "volumes": [
+ "./data-0:/data"
+ ]
+ }
+ }
+ ],
+ "top_level": {
+ "name": "campfire"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "campfire-volume-0",
+ "service": "campfire",
+ "container_path": "/data",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "campfire"
+ ],
+ "stop_order": [
+ "campfire"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": []
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Campfire first-run setup",
+ "scheme": "http",
+ "port": 8080,
+ "path": "/auth/setup",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": []
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 1024,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 120
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/readyz",
+ "timeout_seconds": 240,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 4,
+ "verify_tls": false
+ },
+ "volume_owner": {
+ "uid": 1000,
+ "gid": 1000
+ },
+ "volume_preparations": [
+ {
+ "container_path": "/data",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-application-user",
+ "mode": "2775",
+ "only_when_mount_type": "managed-volume"
+ }
+ ]
+ },
+ "catalog": {
+ "replaces_discovered_ids": []
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/curated/chhoto-url.json b/oci/catalog/curated/chhoto-url.json
new file mode 100644
index 00000000..26f8caec
--- /dev/null
+++ b/oci/catalog/curated/chhoto-url.json
@@ -0,0 +1,500 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-chhoto-url",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Chhoto URL",
+ "es_ES": "Chhoto URL"
+ },
+ "tagline": {
+ "en_US": "A lightweight self-hosted URL shortener with SQLite storage.",
+ "es_ES": "Un acortador de enlaces ligero y autoalojado con almacenamiento SQLite."
+ },
+ "description": {
+ "en_US": "Create and manage short links from a small web interface. Chhoto URL stores its link database in SQLite and supports password-protected administration without a separate database service.",
+ "es_ES": "Crea y administra enlaces cortos desde una interfaz web sencilla. Chhoto URL guarda los enlaces en SQLite y protege la administración con contraseña, sin necesitar otra base de datos."
+ },
+ "category": "tools",
+ "category_label": "Tools",
+ "author": "sintan1729",
+ "developer": "sintan1729",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/chhoto-url.webp",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 4567,
+ "path": "/"
+ },
+ "website": "https://github.com/SinTan1729/chhoto-url",
+ "documentation": "https://github.com/SinTan1729/chhoto-url/blob/main/docs/INSTALLATION.md",
+ "repository": "https://github.com/SinTan1729/chhoto-url",
+ "tips": [
+ "The installer generates the required administrator password. Use HTTPS through a reverse proxy when sending the password over a network; upstream notes that it is not encrypted in plain HTTP."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "sintan1729",
+ "repository": "https://github.com/SinTan1729/chhoto-url",
+ "revision": "611ac292c2dc998fac78a3f726afc3c61a344991",
+ "image_repository_url": "https://hub.docker.com/r/sintan1729/chhoto-url",
+ "readme_pushed_at": "",
+ "default_branch": "main",
+ "compose_sha256": "ccfc1092edaa68277222f013a8c42a4f33c01f0e70bfffb78188c33643f28a87",
+ "generated_at": "2026-10-26T08:45:03+00:00",
+ "readme_raw_url": "https://raw.githubusercontent.com/SinTan1729/chhoto-url/main/README.md"
+ },
+ "container_contract": {
+ "service_name": "chhoto-url",
+ "container_name": "chhoto-url",
+ "image": {
+ "reference": "sintan1729/chhoto-url:latest",
+ "registry": "docker.io",
+ "repository": "sintan1729/chhoto-url",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "CHHOTO_PASSWORD",
+ "example": "${GENERATED_CHHOTO_PASSWORD}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "CHHOTO_DB_URL",
+ "example": "/data/urls.sqlite",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt_user": false
+ },
+ {
+ "name": "CHHOTO_SQLITE_USE_WAL_MODE",
+ "example": "True",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt_user": false
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/data",
+ "compose_source_example": "data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 4
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 4567,
+ "published_example": 4567,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "services:\n chhoto-url:\n image: sintan1729/chhoto-url:latest\n container_name: chhoto-url\n restart: unless-stopped\n ports:\n - 4567:4567/tcp\n volumes:\n - ./data:/data\n environment:\n CHHOTO_PASSWORD: ${GENERATED_CHHOTO_PASSWORD}\n CHHOTO_DB_URL: /data/urls.sqlite\n CHHOTO_SQLITE_USE_WAL_MODE: 'True'\n"
+ },
+ "compose_stack": {
+ "project_name": "chhoto-url",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "chhoto-url",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "chhoto-url",
+ "image": "sintan1729/chhoto-url:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "environment": {
+ "CHHOTO_PASSWORD": "${GENERATED_CHHOTO_PASSWORD}",
+ "CHHOTO_DB_URL": "/data/urls.sqlite",
+ "CHHOTO_SQLITE_USE_WAL_MODE": "True"
+ },
+ "image": "sintan1729/chhoto-url:latest",
+ "ports": [
+ "4567:4567/tcp"
+ ],
+ "volumes": [
+ "./data:/data"
+ ],
+ "container_name": "chhoto-url",
+ "restart": "unless-stopped"
+ }
+ }
+ ],
+ "top_level": {
+ "name": "chhoto-url"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "chhoto-url-volume-0",
+ "service": "chhoto-url",
+ "container_path": "/data",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "chhoto-url"
+ ],
+ "stop_order": [
+ "chhoto-url"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "generated-chhoto-password",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "chhoto-url",
+ "environment_variable": "CHHOTO_PASSWORD"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Chhoto URL",
+ "scheme": "http",
+ "port": 4567,
+ "path": "/",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": [
+ {
+ "label": "Chhoto URL administrator password",
+ "type": "configured-or-installer-generated",
+ "username": "Not required (password only)",
+ "password": null,
+ "password_environment": "CHHOTO_PASSWORD",
+ "change_required": false,
+ "source": "proxmenux-installer-generated",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 512,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "volume_preparations": [
+ {
+ "container_path": "/data",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-root",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "generated_sensitive_environment": {
+ "CHHOTO_PASSWORD": {
+ "strategy": "token-hex",
+ "bytes": 24,
+ "prompt": false
+ }
+ },
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 4567,
+ "path": "/",
+ "timeout_seconds": 180,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/curated/ech0.json b/oci/catalog/curated/ech0.json
new file mode 100644
index 00000000..c20f649e
--- /dev/null
+++ b/oci/catalog/curated/ech0.json
@@ -0,0 +1,482 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-ech0",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Ech0",
+ "es_ES": "Ech0"
+ },
+ "tagline": {
+ "en_US": "A personal, self-hosted microblog and timeline.",
+ "es_ES": "Un microblog y una cronología personal que alojas en tu propio servidor."
+ },
+ "description": {
+ "en_US": "Publish short posts, links and media on a personal timeline that you control. Ech0 includes accounts, comments, RSS and local data storage in one lightweight service.",
+ "es_ES": "Publica textos breves, enlaces y contenido multimedia en una cronología propia. Ech0 incluye cuentas, comentarios, RSS y almacenamiento local en un único servicio."
+ },
+ "category": "communication",
+ "category_label": "Communication & Community",
+ "author": "lin-snow",
+ "developer": "lin-snow",
+ "icon": "https://raw.githubusercontent.com/lin-snow/Ech0/main/docs/imgs/logo.svg",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 6277,
+ "path": "/"
+ },
+ "website": "https://www.ech0.app/",
+ "documentation": "https://github.com/lin-snow/Ech0/blob/main/DEPLOYMENT.md",
+ "repository": "https://github.com/lin-snow/Ech0",
+ "tips": [
+ "The first registered account becomes the Owner. Publishing is restricted to privileged accounts by default.",
+ "Keep the generated JWT secret and the /app/data volume persistent; back up the data volume and retain the same secret across restarts."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "lin-snow",
+ "repository": "https://github.com/lin-snow/Ech0",
+ "revision": "f2da207f4ab7bd89053898dd1e18ffb4ded1204d",
+ "image_repository_url": "https://hub.docker.com/r/sn0wl1n/ech0",
+ "readme_pushed_at": "",
+ "default_branch": "main",
+ "compose_sha256": "3ccb02dfdb7cd5198b2181a1a2f78e0b3a7e205efaf6920c1ca1257a4416b0bd",
+ "generated_at": "2026-10-26T09:32:51+00:00",
+ "readme_raw_url": "https://raw.githubusercontent.com/lin-snow/Ech0/main/README.md"
+ },
+ "container_contract": {
+ "service_name": "ech0",
+ "container_name": "ech0",
+ "image": {
+ "reference": "sn0wl1n/ech0:latest",
+ "registry": "docker.io",
+ "repository": "sn0wl1n/ech0",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "JWT_SECRET",
+ "example": "${GENERATED_JWT_SECRET}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/app/data",
+ "compose_source_example": "data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 6277,
+ "published_example": 6277,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "services:\n ech0:\n image: sn0wl1n/ech0:latest\n container_name: ech0\n restart: unless-stopped\n ports:\n - 6277:6277/tcp\n volumes:\n - ./data:/app/data\n environment:\n JWT_SECRET: ${GENERATED_JWT_SECRET}\n"
+ },
+ "compose_stack": {
+ "project_name": "ech0",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "ech0",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "ech0",
+ "image": "sn0wl1n/ech0:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "sn0wl1n/ech0:latest",
+ "ports": [
+ "6277:6277/tcp"
+ ],
+ "volumes": [
+ "./data:/app/data"
+ ],
+ "container_name": "ech0",
+ "restart": "unless-stopped",
+ "environment": {
+ "JWT_SECRET": "${GENERATED_JWT_SECRET}"
+ }
+ }
+ }
+ ],
+ "top_level": {
+ "name": "ech0"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "ech0-volume-0",
+ "service": "ech0",
+ "container_path": "/app/data",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "ech0"
+ ],
+ "stop_order": [
+ "ech0"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "generated-jwt-secret",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "ech0",
+ "environment_variable": "JWT_SECRET"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Ech0",
+ "scheme": "http",
+ "port": 6277,
+ "path": "/",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": [
+ {
+ "label": "First Ech0 account (becomes Owner)",
+ "type": "configured-or-installer-generated",
+ "username": "Created during first-run setup",
+ "password": null,
+ "change_required": false,
+ "source": "upstream-documentation",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 512,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "volume_preparations": [
+ {
+ "container_path": "/app/data",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-root",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "generated_sensitive_environment": {
+ "JWT_SECRET": {
+ "strategy": "token-hex",
+ "bytes": 32,
+ "prompt": false
+ }
+ },
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 6277,
+ "path": "/",
+ "timeout_seconds": 180,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/curated/kopia.json b/oci/catalog/curated/kopia.json
new file mode 100644
index 00000000..89fd5d55
--- /dev/null
+++ b/oci/catalog/curated/kopia.json
@@ -0,0 +1,540 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-kopia",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Kopia",
+ "es_ES": "Kopia"
+ },
+ "tagline": {
+ "en_US": "Create encrypted, deduplicated backups and restore files.",
+ "es_ES": "Crea copias cifradas y deduplicadas y restaura archivos."
+ },
+ "description": {
+ "en_US": "Kopia provides encrypted, deduplicated backups with a web interface for repositories, policies, snapshots and restores. This profile supports browsing and restoring files through the UI; mounting snapshots with FUSE is not enabled.",
+ "es_ES": "Kopia ofrece copias cifradas y deduplicadas, con una interfaz web para repositorios, políticas, snapshots y restauraciones. Este perfil permite explorar y restaurar archivos desde la interfaz; no habilita el montaje de snapshots mediante FUSE."
+ },
+ "category": "backup",
+ "category_label": "Backup & Recovery",
+ "author": "kopia",
+ "developer": "kopia",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kopia.webp",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 51515,
+ "path": "/"
+ },
+ "website": "https://github.com/kopia/kopia",
+ "documentation": "https://kopia.io/docs/installation/",
+ "repository": "https://github.com/kopia/kopia",
+ "tips": [
+ "The installer generates a web UI password and a separate repository encryption password. Keep the Proxmox LXC configuration and /app/config volume in backups; without the repository password, encrypted snapshots cannot be opened.",
+ "On first visit, create a repository in Kopia UI at /repository, then add /data as a read-only source and choose /restore for restores. Choose a host bind for /data and for /repository if you want to back up a NAS or other host directory.",
+ "The recipe keeps the LXC unprivileged and does not configure FUSE snapshot mounting. Use Kopia UI restore workflows rather than mounting snapshots into the host.",
+ "The server uses HTTP on its LXC address. Keep it on a trusted LAN or put it behind an HTTPS reverse proxy before allowing access from untrusted networks.",
+ "This profile uses the web interface to browse and restore files. It does not enable mounting snapshots with FUSE or shared mount propagation.",
+ "Este perfil usa la interfaz web para explorar y restaurar archivos. No habilita el montaje de snapshots con FUSE ni la propagación compartida de montajes."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "kopia",
+ "repository": "https://github.com/kopia/kopia",
+ "revision": "0835005737025dc3e5203df0138c8265686cb4b3",
+ "image_repository_url": "https://hub.docker.com/r/kopia/kopia",
+ "readme_pushed_at": "",
+ "compose_sha256": "e17828b2fb52ceea822dc412d87549dabe2fb0f404ba16b21272e01d8a78b5c8",
+ "generated_at": "2026-10-26T06:50:27+00:00",
+ "readme_raw_url": "https://kopia.io/docs/installation/"
+ },
+ "container_contract": {
+ "service_name": "kopia",
+ "container_name": "kopia",
+ "image": {
+ "reference": "kopia/kopia:latest",
+ "registry": "docker.io",
+ "repository": "kopia/kopia",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "KOPIA_PASSWORD",
+ "example": "${GENERATED_KOPIA_PASSWORD}",
+ "required": true,
+ "sensitive": true,
+ "source": "official-docker-documentation"
+ },
+ {
+ "name": "KOPIA_SERVER_USERNAME",
+ "example": "kopia",
+ "required": true,
+ "sensitive": false,
+ "source": "official-kopia-cli",
+ "prompt_user": false
+ },
+ {
+ "name": "KOPIA_SERVER_PASSWORD",
+ "example": "${GENERATED_KOPIA_SERVER_PASSWORD}",
+ "required": true,
+ "sensitive": true,
+ "source": "official-kopia-cli"
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/app/config",
+ "compose_source_example": "kopia-config",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 2
+ }
+ },
+ {
+ "id": "volume-1",
+ "container_path": "/app/cache",
+ "compose_source_example": "kopia-cache",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 4
+ }
+ },
+ {
+ "id": "volume-2",
+ "container_path": "/app/logs",
+ "compose_source_example": "kopia-logs",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 2
+ }
+ },
+ {
+ "id": "volume-3",
+ "container_path": "/data",
+ "compose_source_example": "./backup-source",
+ "read_only": true,
+ "required": true,
+ "installation_choice": [
+ "host-bind"
+ ],
+ "default": "host-bind",
+ "managed_volume": {
+ "backup": false,
+ "default_size_gb": 1
+ }
+ },
+ {
+ "id": "volume-4",
+ "container_path": "/repository",
+ "compose_source_example": "./kopia-repository",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 16
+ }
+ },
+ {
+ "id": "volume-5",
+ "container_path": "/restore",
+ "compose_source_example": "kopia-restore",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 51515,
+ "published_example": 51515,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "{\n \"name\": \"kopia\",\n \"services\": {\n \"kopia\": {\n \"image\": \"kopia/kopia:latest\",\n \"container_name\": \"kopia\",\n \"hostname\": \"kopia\",\n \"restart\": \"unless-stopped\",\n \"command\": [\n \"server\",\n \"start\",\n \"--async-repo-connect\",\n \"--address=0.0.0.0:51515\",\n \"--server-username=kopia\"\n ],\n \"environment\": {\n \"KOPIA_PASSWORD\": \"${GENERATED_KOPIA_PASSWORD}\",\n \"KOPIA_SERVER_USERNAME\": \"kopia\",\n \"KOPIA_SERVER_PASSWORD\": \"${GENERATED_KOPIA_SERVER_PASSWORD}\"\n },\n \"ports\": [\n \"51515:51515/tcp\"\n ],\n \"volumes\": [\n \"kopia-config:/app/config\",\n \"kopia-cache:/app/cache\",\n \"kopia-logs:/app/logs\",\n \"./backup-source:/data:ro\",\n \"kopia-repository:/repository\",\n \"kopia-restore:/restore\"\n ]\n }\n }\n}\n"
+ },
+ "compose_stack": {
+ "project_name": "kopia",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "kopia",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "kopia",
+ "image": "kopia/kopia:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "kopia/kopia:latest",
+ "container_name": "kopia",
+ "hostname": "kopia",
+ "restart": "unless-stopped",
+ "command": [
+ "server",
+ "start",
+ "--async-repo-connect",
+ "--address=0.0.0.0:51515",
+ "--server-username=kopia",
+ "--insecure"
+ ],
+ "environment": {
+ "KOPIA_PASSWORD": "${GENERATED_KOPIA_PASSWORD}",
+ "KOPIA_SERVER_USERNAME": "kopia",
+ "KOPIA_SERVER_PASSWORD": "${GENERATED_KOPIA_SERVER_PASSWORD}"
+ },
+ "ports": [
+ "51515:51515/tcp"
+ ],
+ "volumes": [
+ "kopia-config:/app/config",
+ "kopia-cache:/app/cache",
+ "kopia-logs:/app/logs",
+ "./backup-source:/data:ro",
+ "kopia-repository:/repository",
+ "kopia-restore:/restore"
+ ]
+ }
+ }
+ ],
+ "top_level": {
+ "name": "kopia"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "kopia-volume-0",
+ "service": "kopia",
+ "container_path": "/app/config",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ },
+ {
+ "id": "kopia-volume-1",
+ "service": "kopia",
+ "container_path": "/app/cache",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ },
+ {
+ "id": "kopia-volume-2",
+ "service": "kopia",
+ "container_path": "/app/logs",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ },
+ {
+ "id": "kopia-volume-3",
+ "service": "kopia",
+ "container_path": "/data",
+ "mode": "host-bind",
+ "user_selectable": false,
+ "backup": false,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ },
+ {
+ "id": "kopia-volume-4",
+ "service": "kopia",
+ "container_path": "/repository",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ },
+ {
+ "id": "kopia-volume-5",
+ "service": "kopia",
+ "container_path": "/restore",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "kopia"
+ ],
+ "stop_order": [
+ "kopia"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "kopia-password",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "kopia",
+ "environment_variable": "KOPIA_PASSWORD"
+ }
+ ]
+ },
+ {
+ "id": "kopia-server-password",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "kopia",
+ "environment_variable": "KOPIA_SERVER_PASSWORD"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Kopia Web UI",
+ "scheme": "http",
+ "port": 51515,
+ "path": "/",
+ "source": "official-docker-documentation"
+ }
+ ],
+ "credentials": [
+ {
+ "label": "Kopia Web UI login",
+ "type": "configured-or-installer-generated",
+ "username": "kopia",
+ "password": null,
+ "password_environment": "KOPIA_SERVER_PASSWORD",
+ "change_required": false,
+ "source": "official-kopia-cli",
+ "retrieval": null
+ },
+ {
+ "label": "Repository encryption password",
+ "type": "configured-or-installer-generated",
+ "username": "Not applicable",
+ "password": null,
+ "password_environment": "KOPIA_PASSWORD",
+ "change_required": false,
+ "source": "official-docker-documentation",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 2048,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 120
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "The Compose definition publishes service ports on its Docker host.",
+ "native_lxc_behavior": "ProxMenux keeps the image service ports and exposes them on this LXC address without Docker port NAT.",
+ "reason": "The application runs in its own Proxmox LXC network namespace.",
+ "behavioral_impact": "Connect to the LXC address and the service port shown in the install summary.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose provides the command that starts the service.",
+ "native_lxc_behavior": "ProxMenux imports the image entrypoint and applies the reviewed command as the native OCI process.",
+ "reason": "The image runs directly as a native OCI LXC process.",
+ "behavioral_impact": "The process starts with the upstream server command.",
+ "validation": "pending-per-application"
+ }
+ ],
+ "installer_profile": {
+ "generated_sensitive_environment": {
+ "KOPIA_PASSWORD": {
+ "strategy": "token-hex",
+ "bytes": 32,
+ "prompt": false
+ },
+ "KOPIA_SERVER_PASSWORD": {
+ "strategy": "token-hex",
+ "bytes": 24,
+ "prompt": true
+ }
+ },
+ "runtime": {
+ "command": [
+ "server",
+ "start",
+ "--async-repo-connect",
+ "--address=0.0.0.0:51515",
+ "--server-username=kopia",
+ "--insecure"
+ ],
+ "hostname": "kopia"
+ },
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 51515,
+ "path": "/",
+ "timeout_seconds": 240,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "command",
+ "container_name",
+ "environment",
+ "hostname",
+ "image",
+ "ports",
+ "restart",
+ "volumes"
+ ],
+ "untranslated_blockers": [],
+ "policy": "The single upstream image runs as a native Proxmox OCI LXC. Only the documented image command, environment, ports and persistent paths are translated."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/curated/metabase.json b/oci/catalog/curated/metabase.json
new file mode 100644
index 00000000..8629d34b
--- /dev/null
+++ b/oci/catalog/curated/metabase.json
@@ -0,0 +1,534 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-metabase",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Metabase",
+ "es_ES": "Metabase"
+ },
+ "tagline": {
+ "en_US": "Build reports and dashboards from your data.",
+ "es_ES": "Crea informes y paneles a partir de tus datos."
+ },
+ "description": {
+ "en_US": "An open-source business intelligence platform for querying data and building dashboards. This single-container profile uses embedded H2 for a home-lab trial or small instance; PostgreSQL is recommended for production.",
+ "es_ES": "Plataforma de inteligencia de negocio de código abierto para consultar datos y crear paneles. Este perfil de un solo contenedor usa H2 integrada para pruebas o instalaciones pequeñas; para producción se recomienda PostgreSQL."
+ },
+ "category": "monitoring",
+ "category_label": "Monitoring & Analytics",
+ "author": "Metabase",
+ "developer": "Metabase",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/metabase.webp",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 3000,
+ "path": "/"
+ },
+ "website": "https://www.metabase.com/",
+ "documentation": "https://www.metabase.com/docs/latest/installation-and-operation/running-metabase-on-docker",
+ "repository": "https://github.com/metabase/metabase",
+ "tips": [
+ "This profile uses the built-in H2 application database for a simple single-container home-lab setup. Metabase recommends PostgreSQL for production; its application database stores users, questions, dashboards and settings.",
+ "On first visit, complete the setup wizard and create the administrator account. Keep the service on a trusted LAN until setup and authentication are complete.",
+ "The H2 database is stored at /metabase-data/metabase.db. Keep the managed volume persistent and back it up before upgrades or migration.",
+ "ProxMenux generates MB_ENCRYPTION_SECRET_KEY at installation. Keep that key with your backups; losing it can prevent Metabase from decrypting saved database connection credentials.",
+ "The image follows the v0.64 LTS release track. The installer gives it 2 GiB RAM and caps the Java heap at 1 GiB, leaving memory for the rest of the JVM.",
+ "The embedded H2 application database is suitable for a trial or small instance. Metabase recommends PostgreSQL for production; this profile does not add a separate database service.",
+ "La base de datos H2 integrada sirve para pruebas o instalaciones pequeñas. Metabase recomienda PostgreSQL para producción; este perfil no añade una base de datos aparte."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "metabase",
+ "repository": "https://github.com/metabase/metabase",
+ "revision": "9fe55abf5c14d7b4a585be9736affcdd0890be36",
+ "image_repository_url": "https://hub.docker.com/r/metabase/metabase",
+ "readme_pushed_at": "",
+ "compose_sha256": "eb8044f4c7c178e640b24d7cd0e6ce2edf51aafcf4f30ee112f5f1e37b4a2dd3",
+ "generated_at": "2026-10-26T07:19:57+00:00",
+ "readme_raw_url": "https://www.metabase.com/docs/latest/installation-and-operation/running-metabase-on-docker"
+ },
+ "container_contract": {
+ "service_name": "metabase",
+ "container_name": "metabase",
+ "image": {
+ "reference": "metabase/metabase:v0.64-lts",
+ "registry": "docker.io",
+ "repository": "metabase/metabase",
+ "tag": "v0.64-lts",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "MB_DB_TYPE",
+ "example": "h2",
+ "required": true,
+ "sensitive": false,
+ "source": "docker-compose"
+ },
+ {
+ "name": "MB_DB_FILE",
+ "example": "/metabase-data/metabase.db",
+ "required": true,
+ "sensitive": false,
+ "source": "docker-compose"
+ },
+ {
+ "name": "MB_ENCRYPTION_SECRET_KEY",
+ "example": "${GENERATED_MB_ENCRYPTION_SECRET_KEY}",
+ "required": true,
+ "sensitive": true,
+ "source": "docker-compose"
+ },
+ {
+ "name": "MUID",
+ "example": "2000",
+ "required": true,
+ "sensitive": false,
+ "source": "docker-compose"
+ },
+ {
+ "name": "MGID",
+ "example": "2000",
+ "required": true,
+ "sensitive": false,
+ "source": "docker-compose"
+ },
+ {
+ "name": "JAVA_OPTS",
+ "example": "-Xmx1g",
+ "required": true,
+ "sensitive": false,
+ "source": "docker-compose"
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/metabase-data",
+ "compose_source_example": "metabase-data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 3000,
+ "published_example": 3000,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "name: metabase\nservices:\n metabase:\n image: metabase/metabase:v0.64-lts\n restart: unless-stopped\n ports:\n - 3000:3000/tcp\n volumes:\n - metabase-data:/metabase-data\n environment:\n MB_DB_TYPE: h2\n MB_DB_FILE: /metabase-data/metabase.db\n MB_ENCRYPTION_SECRET_KEY: ${GENERATED_MB_ENCRYPTION_SECRET_KEY}\n MUID: '2000'\n MGID: '2000'\n JAVA_OPTS: -Xmx1g\n healthcheck:\n test:\n - CMD-SHELL\n - curl --fail -I http://localhost:3000/api/health || exit 1\n interval: 15s\n timeout: 5s\n retries: 5\n start_period: 180s\nvolumes:\n metabase-data: {}\n"
+ },
+ "compose_stack": {
+ "project_name": "metabase",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "metabase",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "metabase",
+ "image": "metabase/metabase:v0.64-lts",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "metabase/metabase:v0.64-lts",
+ "restart": "unless-stopped",
+ "ports": [
+ "3000:3000/tcp"
+ ],
+ "volumes": [
+ "metabase-data:/metabase-data"
+ ],
+ "environment": {
+ "MB_DB_TYPE": "h2",
+ "MB_DB_FILE": "/metabase-data/metabase.db",
+ "MB_ENCRYPTION_SECRET_KEY": "${GENERATED_MB_ENCRYPTION_SECRET_KEY}",
+ "MUID": "2000",
+ "MGID": "2000",
+ "JAVA_OPTS": "-Xmx1g"
+ },
+ "healthcheck": {
+ "test": [
+ "CMD-SHELL",
+ "curl --fail -I http://localhost:3000/api/health || exit 1"
+ ],
+ "interval": "15s",
+ "timeout": "5s",
+ "retries": 5,
+ "start_period": "180s"
+ }
+ }
+ }
+ ],
+ "top_level": {
+ "name": "metabase",
+ "volumes": {
+ "metabase-data": {}
+ }
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "metabase-volume-0",
+ "service": "metabase",
+ "container_path": "/metabase-data",
+ "mode": "managed-volume",
+ "user_selectable": false,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "metabase"
+ ],
+ "stop_order": [
+ "metabase"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "mb-encryption-secret-key",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "metabase",
+ "environment_variable": "MB_ENCRYPTION_SECRET_KEY"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Web UI",
+ "scheme": "http",
+ "port": 3000,
+ "path": "/",
+ "source": "compose-metadata"
+ }
+ ],
+ "credentials": []
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 2048,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "installer_profile": {
+ "startup_healthcheck": {
+ "type": "http",
+ "scheme": "http",
+ "port": 3000,
+ "path": "/api/health",
+ "timeout_seconds": 300,
+ "request_timeout_seconds": 5,
+ "stability_seconds": 0,
+ "verify_tls": false,
+ "required": true,
+ "source": "compose-healthcheck"
+ },
+ "volume_preparations": [
+ {
+ "container_path": "/metabase-data",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-application-user",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "volume_owner": {
+ "uid": 2000,
+ "gid": 2000
+ },
+ "generated_sensitive_environment": {
+ "MB_ENCRYPTION_SECRET_KEY": {
+ "strategy": "token-hex",
+ "bytes": 32,
+ "prompt": false
+ }
+ }
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ]
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/curated/openhabittracker.json b/oci/catalog/curated/openhabittracker.json
new file mode 100644
index 00000000..4963b840
--- /dev/null
+++ b/oci/catalog/curated/openhabittracker.json
@@ -0,0 +1,533 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-openhabittracker",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "OpenHabitTracker",
+ "es_ES": "OpenHabitTracker"
+ },
+ "tagline": {
+ "en_US": "A self-hosted tracker for habits, tasks, notes and goals.",
+ "es_ES": "Seguimiento autoalojado de hábitos, tareas, notas y objetivos."
+ },
+ "description": {
+ "en_US": "The official image includes the Blazor Server application for one user. It persists application data and backups in /app/.OpenHabitTracker.",
+ "es_ES": "La imagen oficial incluye la aplicación Blazor Server para una persona. Guarda los datos y las copias de seguridad en /app/.OpenHabitTracker."
+ },
+ "category": "productivity",
+ "category_label": "Productivity & Workflows",
+ "author": "Jinjinov",
+ "developer": "Jinjinov",
+ "icon": "https://cdn.jsdelivr.net/gh/Jinjinov/OpenHabitTracker@main/OpenHabitTracker.Web/icons/icon-512.png",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/login"
+ },
+ "website": "https://github.com/Jinjinov/OpenHabitTracker",
+ "documentation": "https://github.com/Jinjinov/OpenHabitTracker/blob/main/README.md",
+ "repository": "https://github.com/Jinjinov/OpenHabitTracker",
+ "tips": [
+ "The installer sets the initial username, email and password. Sign in at /login after startup."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "Jinjinov",
+ "repository": "https://github.com/Jinjinov/OpenHabitTracker",
+ "revision": "a285bf78c04ecebf3b3acc701f1776d4da326905",
+ "image_repository_url": "https://hub.docker.com/r/jinjinov/openhabittracker",
+ "readme_pushed_at": "",
+ "default_branch": "main",
+ "compose_sha256": "1405f9aea98cdf7cc414b9c00a13a1b5086cba2de51f3ba6d1d607e48c94a5d8",
+ "generated_at": "2026-10-26T08:45:03+00:00",
+ "readme_raw_url": "https://raw.githubusercontent.com/Jinjinov/OpenHabitTracker/main/README.md"
+ },
+ "container_contract": {
+ "service_name": "openhabittracker",
+ "container_name": "openhabittracker",
+ "image": {
+ "reference": "jinjinov/openhabittracker:latest",
+ "registry": "docker.io",
+ "repository": "jinjinov/openhabittracker",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "AppSettings__UserName",
+ "example": "",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt": "OpenHabitTracker initial username"
+ },
+ {
+ "name": "AppSettings__Email",
+ "example": "",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt": "Email address for the OpenHabitTracker account"
+ },
+ {
+ "name": "AppSettings__Password",
+ "example": "${GENERATED_APPSETTINGS_PASSWORD}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "AppSettings__JwtSecret",
+ "example": "${GENERATED_APPSETTINGS_JWT_SECRET}",
+ "required": true,
+ "sensitive": true,
+ "source": "upstream-documentation"
+ },
+ {
+ "name": "TZ",
+ "example": "Europe/Madrid",
+ "required": true,
+ "sensitive": false,
+ "source": "upstream-documentation",
+ "prompt_user": false
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/app/.OpenHabitTracker",
+ "compose_source_example": "data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 8080,
+ "published_example": 8080,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "services:\n openhabittracker:\n image: jinjinov/openhabittracker:latest\n container_name: openhabittracker\n restart: unless-stopped\n ports:\n - 8080:8080/tcp\n volumes:\n - ./data:/app/.OpenHabitTracker\n environment:\n AppSettings__UserName: ${AppSettings__UserName}\n AppSettings__Email: ${AppSettings__Email}\n AppSettings__Password: ${GENERATED_APPSETTINGS_PASSWORD}\n AppSettings__JwtSecret: ${GENERATED_APPSETTINGS_JWT_SECRET}\n TZ: Europe/Madrid\n"
+ },
+ "compose_stack": {
+ "project_name": "openhabittracker",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "openhabittracker",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "openhabittracker",
+ "image": "jinjinov/openhabittracker:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "environment": {
+ "AppSettings__UserName": "${AppSettings__UserName}",
+ "AppSettings__Email": "${AppSettings__Email}",
+ "AppSettings__Password": "${GENERATED_APPSETTINGS_PASSWORD}",
+ "AppSettings__JwtSecret": "${GENERATED_APPSETTINGS_JWT_SECRET}",
+ "TZ": "Europe/Madrid"
+ },
+ "image": "jinjinov/openhabittracker:latest",
+ "ports": [
+ "8080:8080/tcp"
+ ],
+ "volumes": [
+ "./data:/app/.OpenHabitTracker"
+ ],
+ "container_name": "openhabittracker",
+ "restart": "unless-stopped"
+ }
+ }
+ ],
+ "top_level": {
+ "name": "openhabittracker"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "openhabittracker-volume-0",
+ "service": "openhabittracker",
+ "container_path": "/app/.OpenHabitTracker",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "openhabittracker"
+ ],
+ "stop_order": [
+ "openhabittracker"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": [
+ {
+ "id": "generated-appsettings-password",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "openhabittracker",
+ "environment_variable": "AppSettings__Password"
+ }
+ ]
+ },
+ {
+ "id": "generated-appsettings-jwtsecret",
+ "strategy": "generate-cryptographically-random-at-install",
+ "bindings": [
+ {
+ "service": "openhabittracker",
+ "environment_variable": "AppSettings__JwtSecret"
+ }
+ ]
+ }
+ ]
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "OpenHabitTracker",
+ "scheme": "http",
+ "port": 8080,
+ "path": "/login",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": [
+ {
+ "label": "OpenHabitTracker account",
+ "type": "configured-or-installer-generated",
+ "username": "Configured username",
+ "username_environment": "AppSettings__UserName",
+ "password": null,
+ "password_environment": "AppSettings__Password",
+ "change_required": false,
+ "source": "proxmenux-installer-generated",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 512,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "volume_preparations": [
+ {
+ "container_path": "/app/.OpenHabitTracker",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-root",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "generated_sensitive_environment": {
+ "AppSettings__Password": {
+ "strategy": "token-hex",
+ "bytes": 24,
+ "prompt": false
+ },
+ "AppSettings__JwtSecret": {
+ "strategy": "token-hex",
+ "bytes": 32,
+ "prompt": false
+ }
+ },
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 8080,
+ "path": "/login",
+ "timeout_seconds": 180,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/curated/restic-rest-server.json b/oci/catalog/curated/restic-rest-server.json
new file mode 100644
index 00000000..4ebc9455
--- /dev/null
+++ b/oci/catalog/curated/restic-rest-server.json
@@ -0,0 +1,315 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-restic-rest-server",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Restic REST Server",
+ "es_ES": "Restic REST Server"
+ },
+ "tagline": {
+ "en_US": "A REST backend for restic backups; it has no web interface.",
+ "es_ES": "Un backend REST para copias de Restic, sin interfaz web."
+ },
+ "description": {
+ "en_US": "The official rest-server image provides an authenticated HTTP REST backend for restic clients, with persistent repositories and bcrypt-protected user accounts. It exposes an API, not a browser-based application.",
+ "es_ES": "La imagen oficial rest-server ofrece un backend HTTP REST autenticado para clientes Restic, con repositorios persistentes y cuentas protegidas con bcrypt. Expone una API, no una aplicación web."
+ },
+ "category": "backup",
+ "category_label": "Backup & Recovery",
+ "author": "restic",
+ "developer": "restic",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/restic.webp",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": null,
+ "path": "/"
+ },
+ "website": "https://github.com/restic/rest-server",
+ "documentation": "https://github.com/restic/rest-server",
+ "repository": "https://github.com/restic/rest-server",
+ "tips": [
+ "Authentication stays enabled. Before connecting any backup client, open the container console and run create_user ; enter the password at the hidden prompt. Do not pass the password as a command-line argument. The image stores the bcrypt hash in /data/.htpasswd.",
+ "The API has no web interface. Use a restic client with a repository URL that includes the username-specific path, for example rest:http://USER@HOST:8000/USER when private repositories are enabled.",
+ "The upstream default uses HTTP, which sends Basic Authentication credentials in clear text. Use a trusted LAN or route clients through an HTTPS reverse proxy before remote access.",
+ "With --private-repos enabled, each user can access only a matching repository subdirectory. Keep /data persistent; it contains both backups and the authentication file.",
+ "The upstream endpoint uses HTTP. Use an HTTPS reverse proxy for remote clients; the example API URL shown in this entry is HTTP.",
+ "El endpoint original usa HTTP. Para clientes remotos, coloca un proxy HTTPS delante; la URL de ejemplo de esta ficha usa HTTP."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "restic",
+ "repository": "https://github.com/restic/rest-server",
+ "revision": "cfcf3fea23edb404c860e8708e39306717fe3e85",
+ "image_repository_url": "https://hub.docker.com/r/restic/rest-server",
+ "readme_pushed_at": "",
+ "compose_sha256": "f346e67428e1c6e8e265d607401fe5d5289b5c7ad478170abb4984fa8dcb01ad",
+ "generated_at": "2026-10-26T06:50:27+00:00",
+ "readme_raw_url": "https://github.com/restic/rest-server"
+ },
+ "container_contract": {
+ "service_name": "restic-rest-server",
+ "container_name": "restic-rest-server",
+ "image": {
+ "reference": "restic/rest-server:latest",
+ "registry": "docker.io",
+ "repository": "restic/rest-server",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [
+ {
+ "name": "OPTIONS",
+ "example": "--private-repos",
+ "required": true,
+ "sensitive": false,
+ "source": "official-rest-server-documentation",
+ "prompt_user": false
+ }
+ ],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/data",
+ "compose_source_example": "restic-data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 16
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 8000,
+ "published_example": 8000,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "{\n \"name\": \"restic-rest-server\",\n \"services\": {\n \"restic-rest-server\": {\n \"image\": \"restic/rest-server:latest\",\n \"container_name\": \"restic-rest-server\",\n \"restart\": \"unless-stopped\",\n \"ports\": [\n \"8000:8000/tcp\"\n ],\n \"volumes\": [\n \"restic-data:/data\"\n ],\n \"environment\": {\n \"OPTIONS\": \"--private-repos\"\n }\n }\n },\n \"volumes\": {\n \"restic-data\": {}\n }\n}\n"
+ },
+ "compose_stack": {
+ "project_name": "restic-rest-server",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "restic-rest-server",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "restic-rest-server",
+ "image": "restic/rest-server:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "restic/rest-server:latest",
+ "container_name": "restic-rest-server",
+ "restart": "unless-stopped",
+ "ports": [
+ "8000:8000/tcp"
+ ],
+ "volumes": [
+ "restic-data:/data"
+ ],
+ "environment": {
+ "OPTIONS": "--private-repos"
+ }
+ }
+ }
+ ],
+ "top_level": {
+ "name": "restic-rest-server",
+ "volumes": {
+ "restic-data": {}
+ }
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "restic-rest-server-volume-0",
+ "service": "restic-rest-server",
+ "container_path": "/data",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "restic-rest-server"
+ ],
+ "stop_order": [
+ "restic-rest-server"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": []
+ }
+ },
+ "first_run": {
+ "endpoints": [],
+ "credentials": [
+ {
+ "label": "REST API Basic Authentication user",
+ "username": "Create with create_user in the container console",
+ "password": null,
+ "change_required": false,
+ "source": "official-rest-server-documentation",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 1,
+ "memory_mb": 512,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 120
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "The Compose definition publishes service ports on its Docker host.",
+ "native_lxc_behavior": "ProxMenux keeps the image service ports and exposes them on this LXC address without Docker port NAT.",
+ "reason": "The application runs in its own Proxmox LXC network namespace.",
+ "behavioral_impact": "Connect to the LXC address and the service port shown in the install summary.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose provides the command that starts the service.",
+ "native_lxc_behavior": "ProxMenux imports the image entrypoint and applies the reviewed command as the native OCI process.",
+ "reason": "The image runs directly as a native OCI LXC process.",
+ "behavioral_impact": "The process starts with the upstream server command.",
+ "validation": "pending-per-application"
+ }
+ ],
+ "installer_profile": {
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 8000,
+ "path": "/",
+ "timeout_seconds": 120,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "command",
+ "container_name",
+ "environment",
+ "hostname",
+ "image",
+ "ports",
+ "restart",
+ "volumes"
+ ],
+ "untranslated_blockers": [],
+ "policy": "The single upstream image runs as a native Proxmox OCI LXC. Only the documented image command, environment, ports and persistent paths are translated."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/curated/watcharr.json b/oci/catalog/curated/watcharr.json
new file mode 100644
index 00000000..a72d08ff
--- /dev/null
+++ b/oci/catalog/curated/watcharr.json
@@ -0,0 +1,453 @@
+{
+ "schema_version": "0.5.0",
+ "kind": "proxmenux.oci-template",
+ "id": "image-watcharr",
+ "status": "generated-unvalidated",
+ "catalog_ui": {
+ "title": {
+ "en_US": "Watcharr",
+ "es_ES": "Watcharr"
+ },
+ "tagline": {
+ "en_US": "Track movies, television, anime and games you want to enjoy.",
+ "es_ES": "Lleva un registro de películas, series, anime y videojuegos."
+ },
+ "description": {
+ "en_US": "Track movies, television shows, anime and games you want to watch or play. The single container stores its database and cache in /data.",
+ "es_ES": "Haz seguimiento de películas, series, anime y juegos que quieres ver o jugar. El contenedor guarda la base de datos y la caché en /data."
+ },
+ "category": "media",
+ "category_label": "Media & Streaming",
+ "author": "sbondCo",
+ "developer": "sbondCo",
+ "icon": "https://raw.githubusercontent.com/sbondCo/Watcharr/dev/doc/static/img/logo-col.png",
+ "thumbnail": null,
+ "screenshots": [],
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "launch": {
+ "scheme": "http",
+ "port": 3080,
+ "path": "/"
+ },
+ "website": "https://watcharr.app/",
+ "documentation": "https://watcharr.app/docs/installation/docker-compose",
+ "repository": "https://github.com/sbondCo/Watcharr",
+ "tips": [
+ "The first account created becomes the administrator. Keep access limited to your trusted network until initial setup is complete."
+ ],
+ "mini_changelog": [],
+ "display_version": null,
+ "updated_at": null
+ },
+ "source": {
+ "provider": "sbondCo",
+ "repository": "https://github.com/sbondCo/Watcharr",
+ "revision": "aa2a765788a4d0f6eb607e7f90acb0d89d62fcd7",
+ "image_repository_url": "https://github.com/orgs/sbondco/packages/container/package/watcharr",
+ "readme_pushed_at": "",
+ "default_branch": "dev",
+ "compose_sha256": "8c4aa4fc5f19e33730685e75e89a7d6a79253bc3ea7ebd6ea6e27e0981fe5f54",
+ "generated_at": "2026-10-26T08:45:03+00:00",
+ "readme_raw_url": "https://raw.githubusercontent.com/sbondCo/Watcharr/dev/README.md"
+ },
+ "container_contract": {
+ "service_name": "watcharr",
+ "container_name": "watcharr",
+ "image": {
+ "reference": "ghcr.io/sbondco/watcharr:latest",
+ "registry": "ghcr.io",
+ "repository": "ghcr.io/sbondco/watcharr",
+ "tag": "latest",
+ "digest": null,
+ "pull_policy": "resolve-written-reference-to-architecture-digest-at-install"
+ },
+ "environment": [],
+ "volumes": [
+ {
+ "id": "volume-0",
+ "container_path": "/data",
+ "compose_source_example": "data",
+ "read_only": false,
+ "required": true,
+ "installation_choice": [
+ "managed-volume",
+ "host-bind"
+ ],
+ "default": "managed-volume",
+ "managed_volume": {
+ "backup": true,
+ "default_size_gb": 8
+ }
+ }
+ ],
+ "ports": [
+ {
+ "container_port": 3080,
+ "published_example": 3080,
+ "protocol": "tcp",
+ "required": true,
+ "proxmox_behavior": "listener-on-dedicated-lxc-address-no-nat"
+ }
+ ],
+ "related_services": [],
+ "restart": "unless-stopped",
+ "stop_grace_period": null,
+ "original_compose": "services:\n watcharr:\n image: ghcr.io/sbondco/watcharr:latest\n container_name: watcharr\n restart: unless-stopped\n ports:\n - 3080:3080/tcp\n volumes:\n - ./data:/data\n environment: {}\n"
+ },
+ "compose_stack": {
+ "project_name": "watcharr",
+ "deployment_model": "one-native-oci-lxc-per-compose-service",
+ "user_experience": "single-application-install",
+ "main_service": "watcharr",
+ "service_count": 1,
+ "services": [
+ {
+ "name": "watcharr",
+ "image": "ghcr.io/sbondco/watcharr:latest",
+ "is_main": true,
+ "role": "frontend",
+ "vmid_offset": 0,
+ "depends_on": [],
+ "frontend_network": true,
+ "private_network": false,
+ "compose": {
+ "image": "ghcr.io/sbondco/watcharr:latest",
+ "ports": [
+ "3080:3080/tcp"
+ ],
+ "volumes": [
+ "./data:/data"
+ ],
+ "container_name": "watcharr",
+ "restart": "unless-stopped",
+ "environment": {}
+ }
+ }
+ ],
+ "top_level": {
+ "name": "watcharr"
+ },
+ "networking": {
+ "frontend": "selected-proxmox-bridge",
+ "private_required": false,
+ "private_creation": "automatic-create-if-missing",
+ "private_address_allocation": "automatic-static-address-per-service",
+ "service_discovery": "private-addresses-with-compose-service-host-aliases",
+ "dependency_external_access": "disabled-unless-service-publishes-ports",
+ "prompt_user_for_private_network": false
+ },
+ "storage": [
+ {
+ "id": "watcharr-volume-0",
+ "service": "watcharr",
+ "container_path": "/data",
+ "mode": "managed-volume",
+ "user_selectable": true,
+ "backup": true,
+ "shared_with_other_lxc": false,
+ "source_path": null,
+ "source_path_prompt": null
+ }
+ ],
+ "orchestration": {
+ "reserve_vmids_atomically": 1,
+ "start_order": [
+ "watcharr"
+ ],
+ "stop_order": [
+ "watcharr"
+ ],
+ "dependency_readiness": "compose-healthcheck-then-port-or-process-fallback",
+ "rollback_on_failure": "remove-new-rootfs-preserve-created-persistent-volumes"
+ },
+ "installer_inputs": {
+ "prompted": [
+ "stack_name",
+ "base_vmid",
+ "rootfs_storage",
+ "persistent_data_destinations",
+ "frontend_bridge",
+ "frontend_ipv4_mode"
+ ],
+ "automatic": [
+ "dependent_vmids",
+ "private_bridge",
+ "private_subnet",
+ "private_service_addresses",
+ "compose_service_aliases",
+ "generated_secrets",
+ "dependency_start_and_stop_order"
+ ],
+ "generated_secrets": []
+ }
+ },
+ "first_run": {
+ "endpoints": [
+ {
+ "label": "Watcharr",
+ "scheme": "http",
+ "port": 3080,
+ "path": "/",
+ "source": "upstream-documentation"
+ }
+ ],
+ "credentials": [
+ {
+ "label": "First Watcharr account (becomes administrator)",
+ "type": "configured-or-installer-generated",
+ "username": "Created during first-run setup",
+ "password": null,
+ "change_required": false,
+ "source": "upstream-documentation",
+ "retrieval": null
+ }
+ ]
+ },
+ "proxmox": {
+ "runtime": "native-oci-lxc",
+ "technology_status": "proxmox-technology-preview",
+ "defaults": {
+ "unprivileged": true,
+ "ostype": "auto-from-image",
+ "cores": 2,
+ "memory_mb": 512,
+ "swap_mb": 512,
+ "rootfs_size_gb": 8,
+ "rootfs_storage": "local-lvm",
+ "volume_storage": "local-lvm",
+ "template_storage": "local",
+ "bridge": "vmbr0",
+ "ipv4": "dhcp",
+ "firewall": true,
+ "host_managed_network": true,
+ "onboot": false,
+ "features": [
+ "nesting=1"
+ ],
+ "shutdown_timeout_seconds": 30
+ },
+ "image_metadata_policy": {
+ "entrypoint": "import-from-oci-image",
+ "cmd": "import-from-oci-image",
+ "environment": "import-image-env-then-apply-compose-overrides",
+ "user": "import-from-oci-image",
+ "working_dir": "import-from-oci-image",
+ "stop_signal": "import-from-oci-image"
+ },
+ "adaptations": [
+ {
+ "id": "imported-compose-source",
+ "upstream_behavior": "The source definition deploys the complete Docker Compose application model.",
+ "native_lxc_behavior": "The source model is preserved and remains blocked until every service option has a reviewed native Proxmox mapping.",
+ "reason": "Catalog import must not imply runtime compatibility.",
+ "behavioral_impact": "No automatic installation before review.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "rolling-latest-image",
+ "upstream_behavior": "A discovered Compose may pin a release tag or digest.",
+ "native_lxc_behavior": "ProxMenux selects the same image repository with the latest tag for catalog installations.",
+ "reason": "The automatic catalog intentionally offers rolling latest images; pinned versions belong to the future manual installer.",
+ "behavioral_impact": "The installed release can be newer than the discovered Compose revision.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "dedicated-lxc-network",
+ "upstream_behavior": "Docker publishes selected container ports on the Docker host.",
+ "native_lxc_behavior": "A reviewed native application will listen on its original container ports at a dedicated LXC address.",
+ "reason": "A native LXC has its own address and does not require Docker port NAT.",
+ "behavioral_impact": "Published ports are metadata; ProxMenux URLs use the matching container target port.",
+ "validation": "pending-per-application"
+ },
+ {
+ "id": "compose-shm-size",
+ "upstream_behavior": "Compose sets the size of the container /dev/shm tmpfs.",
+ "native_lxc_behavior": "ProxMenux mounts a native LXC tmpfs at /dev/shm with the same requested capacity.",
+ "reason": "The OCI image runs directly as an LXC and therefore needs the equivalent Proxmox mount entry.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-command",
+ "upstream_behavior": "Compose replaces the image Cmd while retaining its Entrypoint.",
+ "native_lxc_behavior": "ProxMenux reads the official OCI Entrypoint and combines it with the Compose command as the native LXC init command.",
+ "reason": "Proxmox stores the effective OCI process as one entrypoint string.",
+ "behavioral_impact": "None expected.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-privileged-mode",
+ "upstream_behavior": "Compose selects whether the container runs in privileged mode.",
+ "native_lxc_behavior": "ProxMenux keeps the LXC unprivileged by default and exposes the broad Compose privilege request only as an explicit compatibility option.",
+ "reason": "A Compose privilege request is source metadata, not proof that the image technically requires a privileged LXC.",
+ "behavioral_impact": "A privileged LXC has weaker isolation from the Proxmox host.",
+ "validation": "native-equivalent"
+ },
+ {
+ "id": "compose-process-runtime",
+ "upstream_behavior": "Compose can replace Entrypoint, User and WorkingDir and request an init process or interactive terminal.",
+ "native_lxc_behavior": "ProxMenux applies the process overrides through native LXC init directives; lxc-init provides PID 1 supervision and the CT console provides terminal access.",
+ "reason": "The OCI process must start with the same identity, command and working directory without Docker.",
+ "behavioral_impact": "Compose stdin_open and tty become access through the Proxmox LXC console.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-healthcheck",
+ "upstream_behavior": "Docker periodically executes the declared container healthcheck.",
+ "native_lxc_behavior": "For a single-service LXC, ProxMenux translates HTTP localhost checks into a mandatory first-start service check.",
+ "reason": "Proxmox has no persistent Docker health state, while the installer still must detect a failed first boot.",
+ "behavioral_impact": "The check runs during installation rather than continuously after installation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-cpu-priority",
+ "upstream_behavior": "Docker cpu_shares sets a relative scheduling weight with 1024 as its neutral value.",
+ "native_lxc_behavior": "ProxMenux converts the relative weight to Proxmox cpuunits with 100 as its neutral value and lets the user review it.",
+ "reason": "Both settings express relative CPU priority on different scales.",
+ "behavioral_impact": "Rounding and Proxmox minimum limits can slightly change very low weights.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-resource-limits",
+ "upstream_behavior": "mem_limit sets the memory ceiling; ulimits sets process soft/hard resource limits.",
+ "native_lxc_behavior": "mem_limit supplies the editable Proxmox memory default, rounded up to MiB; ulimits maps to lxc.prlimit with -1 represented as unlimited.",
+ "reason": "Use native Proxmox memory and LXC prlimits, without a wrapper or changing the host kernel configuration.",
+ "behavioral_impact": "User-selected memory overrides Compose. Swap is a separate choice. Kernel restrictions still apply; nproc counts processes by real UID, not by container.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-identity",
+ "upstream_behavior": "Compose can set hostname, MAC address, extra hosts and attach a service to Docker networks.",
+ "native_lxc_behavior": "ProxMenux applies hostname and MAC to net0, writes additional host aliases into the LXC and uses its dedicated bridge connection for single-service networks.",
+ "reason": "A dedicated LXC has its own network namespace and does not need a Docker bridge per service.",
+ "behavioral_impact": "host-gateway resolves to the IPv4 address of the selected Proxmox bridge.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-network-mode",
+ "upstream_behavior": "Docker host mode removes Docker network isolation; bridge and default use a Docker-managed network.",
+ "native_lxc_behavior": "The OCI process uses the dedicated LXC network namespace directly, so host, bridge and default all listen on the LXC address without Docker NAT.",
+ "reason": "The LXC is the application host and already has its own address and port namespace.",
+ "behavioral_impact": "host means the LXC host, never the Proxmox host; this preserves Proxmox network isolation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-capabilities-and-sysctls",
+ "upstream_behavior": "Compose can add Linux capabilities and set kernel parameters in the container network namespace.",
+ "native_lxc_behavior": "ProxMenux validates requested capabilities against the native LXC capability set and writes namespaced network settings as lxc.sysctl directives.",
+ "reason": "A native OCI-LXC already starts with the namespaced capability set; lxc.cap.keep would incorrectly discard unrelated required capabilities.",
+ "behavioral_impact": "Host-global capabilities such as SYS_MODULE remain blocked until their host prerequisite is explicitly adapted.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "docker-engine-metadata",
+ "upstream_behavior": "Compose labels annotate Docker objects and the json-file logging driver rotates Docker-managed logs.",
+ "native_lxc_behavior": "Labels remain source metadata; Docker json-file settings are not applied because the OCI process runs directly under LXC.",
+ "reason": "There is no Docker object or Docker json-file log behind a native OCI-LXC application.",
+ "behavioral_impact": "Docker-only label consumers and Docker log-driver rotation do not exist in the native deployment.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-device-passthrough",
+ "upstream_behavior": "Compose passes host character devices or requests an NVIDIA runtime GPU.",
+ "native_lxc_behavior": "ProxMenux converts recognized device declarations to native Proxmox dev resources; NVIDIA profiles also inject compatible host driver libraries read-only.",
+ "reason": "Native OCI-LXC does not execute Docker device or NVIDIA runtime hooks.",
+ "behavioral_impact": "Hardware is exposed only after explicit user confirmation and host-path validation.",
+ "validation": "not-requested-by-compose"
+ },
+ {
+ "id": "compose-host-ipc",
+ "upstream_behavior": "ipc: host shares the Docker host IPC namespace, commonly to avoid Docker's small default shared-memory allocation.",
+ "native_lxc_behavior": "The application keeps the LXC IPC namespace and receives a configurable 1 GiB /dev/shm instead of sharing Proxmox host IPC.",
+ "reason": "Processes in a single native LXC already share one IPC namespace; retaining isolation is safer than exposing host IPC.",
+ "behavioral_impact": "The application cannot exchange IPC objects with processes on the Proxmox host.",
+ "validation": "not-requested-by-compose"
+ }
+ ],
+ "installer_profile": {
+ "volume_preparations": [
+ {
+ "container_path": "/data",
+ "remove_lost_found": true,
+ "owner_strategy": "mapped-root",
+ "only_when_mount_type": "managed-volume"
+ }
+ ],
+ "startup_healthcheck": {
+ "scheme": "http",
+ "port": 3080,
+ "path": "/",
+ "timeout_seconds": 180,
+ "request_timeout_seconds": 10,
+ "stability_seconds": 3,
+ "verify_tls": false,
+ "accept_any_status": true
+ }
+ }
+ },
+ "compatibility": {
+ "automatic_install_candidate": true,
+ "validated": false,
+ "supported_compose_keys": [
+ "cap_add",
+ "command",
+ "container_name",
+ "cpu_shares",
+ "deploy",
+ "devices",
+ "entrypoint",
+ "environment",
+ "extra_hosts",
+ "group_add",
+ "healthcheck",
+ "hostname",
+ "image",
+ "init",
+ "ipc",
+ "labels",
+ "logging",
+ "mac_address",
+ "mem_limit",
+ "network_mode",
+ "networks",
+ "ports",
+ "privileged",
+ "restart",
+ "runtime",
+ "security_opt",
+ "shm_size",
+ "stdin_open",
+ "stop_grace_period",
+ "sysctls",
+ "tty",
+ "ulimits",
+ "user",
+ "volumes",
+ "working_dir"
+ ],
+ "untranslated_blockers": [],
+ "policy": "Single-image definitions are installable when every declared Compose option has a native Proxmox translation. Multi-image and unsupported runtime features remain blocked until their orchestrator or mapping is available."
+ },
+ "validation": {
+ "schema": "passed-at-generation",
+ "clean_install": "pending",
+ "service_health": "pending",
+ "restart_persistence": "pending",
+ "backup_restore": "pending",
+ "update_preserves_data": "pending"
+ },
+ "lifecycle": {
+ "update_strategy": "resolve-latest-image-then-apply-reviewed-native-lxc-update",
+ "registry_state": {
+ "resolved_architecture": null,
+ "resolved_digest": null,
+ "image_version_label": null,
+ "image_created": null
+ },
+ "change_detection": "compare-compose-sha256-and-resolved-latest-image-digest",
+ "automatic_unattended_updates": false
+ }
+}
diff --git a/oci/catalog/index.json b/oci/catalog/index.json
index 7c134b53..2dd9fd72 100644
--- a/oci/catalog/index.json
+++ b/oci/catalog/index.json
@@ -71,6 +71,37 @@
"hidden": false,
"template_id": "image-acestream-http-proxy"
},
+ {
+ "id": "activepieces",
+ "provider": "activepieces",
+ "template_family": "curated-profile",
+ "title": "Activepieces",
+ "repository": "https://github.com/activepieces/activepieces",
+ "description": "Build and run automation flows from a self-hosted visual interface. This single-container profile uses Activepieces\u2019 documented personal mode with embedded storage and an in-memory queue.",
+ "website": "https://www.activepieces.com/",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/activepieces.webp",
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "updated_at": null,
+ "main_image": "ghcr.io/activepieces/activepieces:latest",
+ "category": "automation",
+ "category_label": "Automation & Scheduling",
+ "replaces_discovered_ids": [],
+ "curated_path": "catalog/curated/activepieces.json",
+ "template_id": "image-activepieces",
+ "template": "apps/activepieces.json",
+ "template_status": "generated-unvalidated",
+ "automatic_install_candidate": true,
+ "requires_privileged_lxc": false,
+ "optional_privileged_lxc": false,
+ "requires_host_pid_namespace": false,
+ "requires_relaxed_confinement": false,
+ "requires_security_confirmation": false,
+ "hidden": false,
+ "untranslated_blockers": []
+ },
{
"id": "actualbudget",
"source_app_id": "actualbudget",
@@ -1439,6 +1470,38 @@
"hidden": false,
"template_id": "linuxserver-budge"
},
+ {
+ "id": "bytebase",
+ "provider": "Bytebase",
+ "template_family": "curated-profile",
+ "title": "Bytebase",
+ "repository": "https://github.com/bytebase/bytebase",
+ "description": "Manage database changes, access and workflows through a browser-based workspace. The image contains its own database, so this profile does not require a second service.",
+ "website": "https://www.bytebase.com/",
+ "icon": "https://cdn.jsdelivr.net/gh/bytebase/bytebase@main/frontend/src/assets/logo-icon.svg",
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "updated_at": null,
+ "main_image": "bytebase/bytebase:latest",
+ "category": "databases",
+ "category_label": "Databases",
+ "replaces_discovered_ids": [],
+ "curated_path": "catalog/curated/bytebase.json",
+ "template": "apps/bytebase.json",
+ "template_status": "generated-unvalidated",
+ "automatic_install_candidate": true,
+ "untranslated_blockers": [],
+ "requires_privileged_lxc": false,
+ "optional_privileged_lxc": false,
+ "requires_host_pid_namespace": false,
+ "requires_relaxed_confinement": false,
+ "optional_relaxed_confinement": false,
+ "requires_security_confirmation": false,
+ "hidden": false,
+ "template_id": "image-bytebase"
+ },
{
"id": "caddy",
"provider": "caddyserver",
@@ -1599,6 +1662,39 @@
"hidden": false,
"template_id": "image-calnode"
},
+ {
+ "id": "campfire",
+ "provider": "AwsomeFox",
+ "template_family": "curated-profile",
+ "title": "Campfire",
+ "repository": "https://github.com/AwsomeFox/campfire",
+ "description": "A self-hosted web application for organizing tabletop role-playing games.",
+ "website": "https://github.com/AwsomeFox/campfire",
+ "icon": "https://raw.githubusercontent.com/AwsomeFox/campfire/main/apps/web/public/pwa-512x512.png",
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "updated_at": null,
+ "main_image": "ghcr.io/awsomefox/campfire:latest",
+ "category": "gaming",
+ "category_label": "Gaming & Leisure",
+ "replaces_discovered_ids": [],
+ "curated_path": "catalog/curated/campfire.json",
+ "template": "apps/campfire.json",
+ "template_status": "generated-unvalidated",
+ "hidden": false,
+ "automatic_install_candidate": true,
+ "untranslated_blockers": [],
+ "requires_privileged_lxc": false,
+ "optional_privileged_lxc": false,
+ "requires_host_pid_namespace": false,
+ "requires_relaxed_confinement": false,
+ "optional_relaxed_confinement": false,
+ "requires_security_confirmation": false,
+ "multi_container": false,
+ "template_id": "image-campfire"
+ },
{
"id": "changedetection.io",
"provider": "linuxserver.io",
@@ -1735,6 +1831,38 @@
"hidden": false,
"template_id": "image-chatgpt-next-web"
},
+ {
+ "id": "chhoto-url",
+ "provider": "sintan1729",
+ "template_family": "curated-profile",
+ "title": "Chhoto URL",
+ "repository": "https://github.com/SinTan1729/chhoto-url",
+ "description": "Create and manage short links from a small web interface. Chhoto URL stores its link database in SQLite and supports password-protected administration without a separate database service.",
+ "website": "https://github.com/SinTan1729/chhoto-url",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/chhoto-url.webp",
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "updated_at": null,
+ "main_image": "sintan1729/chhoto-url:latest",
+ "category": "tools",
+ "category_label": "Tools",
+ "replaces_discovered_ids": [],
+ "curated_path": "catalog/curated/chhoto-url.json",
+ "template": "apps/chhoto-url.json",
+ "template_status": "generated-unvalidated",
+ "automatic_install_candidate": true,
+ "untranslated_blockers": [],
+ "requires_privileged_lxc": false,
+ "optional_privileged_lxc": false,
+ "requires_host_pid_namespace": false,
+ "requires_relaxed_confinement": false,
+ "optional_relaxed_confinement": false,
+ "requires_security_confirmation": false,
+ "multi_container": false,
+ "template_id": "image-chhoto-url"
+ },
{
"id": "chrome",
"provider": "linuxserver.io",
@@ -3668,6 +3796,38 @@
"hidden": false,
"template_id": "image-duplicati-official"
},
+ {
+ "id": "ech0",
+ "provider": "lin-snow",
+ "template_family": "curated-profile",
+ "title": "Ech0",
+ "repository": "https://github.com/lin-snow/Ech0",
+ "description": "Publish short posts, links and media on a personal timeline that you control. Ech0 includes accounts, comments, RSS and local data storage in one lightweight service.",
+ "website": "https://www.ech0.app/",
+ "icon": "https://raw.githubusercontent.com/lin-snow/Ech0/main/docs/imgs/logo.svg",
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "updated_at": null,
+ "main_image": "sn0wl1n/ech0:latest",
+ "category": "communication",
+ "category_label": "Communication & Community",
+ "replaces_discovered_ids": [],
+ "curated_path": "catalog/curated/ech0.json",
+ "template": "apps/ech0.json",
+ "template_status": "generated-unvalidated",
+ "automatic_install_candidate": true,
+ "untranslated_blockers": [],
+ "requires_privileged_lxc": false,
+ "optional_privileged_lxc": false,
+ "requires_host_pid_namespace": false,
+ "requires_relaxed_confinement": false,
+ "optional_relaxed_confinement": false,
+ "requires_security_confirmation": false,
+ "multi_container": false,
+ "template_id": "image-ech0"
+ },
{
"id": "eden",
"provider": "linuxserver.io",
@@ -7629,6 +7789,38 @@
"hidden": false,
"template_id": "image-komga"
},
+ {
+ "id": "kopia",
+ "provider": "kopia",
+ "template_family": "curated-profile",
+ "title": "Kopia",
+ "repository": "https://github.com/kopia/kopia",
+ "description": "Kopia provides encrypted, deduplicated backups with a web interface for repositories, policies, snapshots and restores. This profile supports browsing and restoring files through the UI; mounting snapshots with FUSE is not enabled.",
+ "website": "https://github.com/kopia/kopia",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kopia.webp",
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "updated_at": null,
+ "main_image": "kopia/kopia:latest",
+ "category": "backup",
+ "category_label": "Backup & Recovery",
+ "replaces_discovered_ids": [],
+ "curated_path": "catalog/curated/kopia.json",
+ "template": "apps/kopia.json",
+ "template_status": "generated-unvalidated",
+ "automatic_install_candidate": true,
+ "untranslated_blockers": [],
+ "requires_privileged_lxc": false,
+ "optional_privileged_lxc": false,
+ "requires_host_pid_namespace": false,
+ "requires_relaxed_confinement": false,
+ "optional_relaxed_confinement": false,
+ "requires_security_confirmation": false,
+ "hidden": false,
+ "template_id": "image-kopia"
+ },
{
"id": "krita",
"provider": "linuxserver.io",
@@ -9320,6 +9512,38 @@
"multi_container": false,
"template_id": "image-meshcentral"
},
+ {
+ "id": "metabase",
+ "provider": "metabase",
+ "template_family": "curated-profile",
+ "title": "Metabase",
+ "repository": "https://github.com/metabase/metabase",
+ "description": "An open-source business intelligence platform for querying data and building dashboards. This single-container profile uses embedded H2 for a home-lab trial or small instance; PostgreSQL is recommended for production.",
+ "website": "https://www.metabase.com/",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/metabase.webp",
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "updated_at": null,
+ "main_image": "metabase/metabase:v0.64-lts",
+ "category": "monitoring",
+ "category_label": "Monitoring & Analytics",
+ "replaces_discovered_ids": [],
+ "curated_path": "catalog/curated/metabase.json",
+ "template": "apps/metabase.json",
+ "template_status": "generated-unvalidated",
+ "automatic_install_candidate": true,
+ "untranslated_blockers": [],
+ "requires_privileged_lxc": false,
+ "optional_privileged_lxc": false,
+ "requires_host_pid_namespace": false,
+ "requires_relaxed_confinement": false,
+ "optional_relaxed_confinement": false,
+ "requires_security_confirmation": false,
+ "hidden": false,
+ "template_id": "image-metabase"
+ },
{
"id": "metube",
"provider": "alexta69",
@@ -11210,6 +11434,38 @@
"hidden": false,
"template_id": "image-openhab"
},
+ {
+ "id": "openhabittracker",
+ "provider": "Jinjinov",
+ "template_family": "curated-profile",
+ "title": "OpenHabitTracker",
+ "repository": "https://github.com/Jinjinov/OpenHabitTracker",
+ "description": "The official image includes the Blazor Server application for one user. It persists application data and backups in /app/.OpenHabitTracker.",
+ "website": "https://github.com/Jinjinov/OpenHabitTracker",
+ "icon": "https://cdn.jsdelivr.net/gh/Jinjinov/OpenHabitTracker@main/OpenHabitTracker.Web/icons/icon-512.png",
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "updated_at": null,
+ "main_image": "jinjinov/openhabittracker:latest",
+ "category": "productivity",
+ "category_label": "Productivity & Workflows",
+ "replaces_discovered_ids": [],
+ "curated_path": "catalog/curated/openhabittracker.json",
+ "template": "apps/openhabittracker.json",
+ "template_status": "generated-unvalidated",
+ "automatic_install_candidate": true,
+ "untranslated_blockers": [],
+ "requires_privileged_lxc": false,
+ "optional_privileged_lxc": false,
+ "requires_host_pid_namespace": false,
+ "requires_relaxed_confinement": false,
+ "optional_relaxed_confinement": false,
+ "requires_security_confirmation": false,
+ "multi_container": false,
+ "template_id": "image-openhabittracker"
+ },
{
"id": "openhands",
"source_app_id": "openhands",
@@ -13732,6 +13988,38 @@
"hidden": false,
"template_id": "linuxserver-resilio-sync"
},
+ {
+ "id": "restic-rest-server",
+ "provider": "restic",
+ "template_family": "curated-profile",
+ "title": "Restic REST Server",
+ "repository": "https://github.com/restic/rest-server",
+ "description": "The official rest-server image provides an authenticated HTTP REST backend for restic clients, with persistent repositories and bcrypt-protected user accounts. It exposes an API, not a browser-based application.",
+ "website": "https://github.com/restic/rest-server",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/restic.webp",
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "updated_at": null,
+ "main_image": "restic/rest-server:latest",
+ "category": "backup",
+ "category_label": "Backup & Recovery",
+ "replaces_discovered_ids": [],
+ "curated_path": "catalog/curated/restic-rest-server.json",
+ "template": "apps/restic-rest-server.json",
+ "template_status": "generated-unvalidated",
+ "automatic_install_candidate": true,
+ "untranslated_blockers": [],
+ "requires_privileged_lxc": false,
+ "optional_privileged_lxc": false,
+ "requires_host_pid_namespace": false,
+ "requires_relaxed_confinement": false,
+ "optional_relaxed_confinement": false,
+ "requires_security_confirmation": false,
+ "hidden": false,
+ "template_id": "image-restic-rest-server"
+ },
{
"id": "retroarch",
"provider": "linuxserver.io",
@@ -17266,6 +17554,38 @@
"hidden": false,
"template_id": "image-wallos"
},
+ {
+ "id": "watcharr",
+ "provider": "sbondCo",
+ "template_family": "curated-profile",
+ "title": "Watcharr",
+ "repository": "https://github.com/sbondCo/Watcharr",
+ "description": "Track movies, television shows, anime and games you want to watch or play. The single container stores its database and cache in /data.",
+ "website": "https://watcharr.app/",
+ "icon": "https://raw.githubusercontent.com/sbondCo/Watcharr/dev/doc/static/img/logo-col.png",
+ "architectures": [
+ "amd64",
+ "arm64"
+ ],
+ "updated_at": null,
+ "main_image": "ghcr.io/sbondco/watcharr:latest",
+ "category": "media",
+ "category_label": "Media & Streaming",
+ "replaces_discovered_ids": [],
+ "curated_path": "catalog/curated/watcharr.json",
+ "template": "apps/watcharr.json",
+ "template_status": "generated-unvalidated",
+ "automatic_install_candidate": true,
+ "untranslated_blockers": [],
+ "requires_privileged_lxc": false,
+ "optional_privileged_lxc": false,
+ "requires_host_pid_namespace": false,
+ "requires_relaxed_confinement": false,
+ "optional_relaxed_confinement": false,
+ "requires_security_confirmation": false,
+ "multi_container": false,
+ "template_id": "image-watcharr"
+ },
{
"id": "wbo",
"provider": "lovasoa",
diff --git a/oci/tests/test_next10_a_profiles.py b/oci/tests/test_next10_a_profiles.py
new file mode 100644
index 00000000..97a27484
--- /dev/null
+++ b/oci/tests/test_next10_a_profiles.py
@@ -0,0 +1,54 @@
+"""Guard the Campfire recipe contract in the next-10 catalog profiles."""
+import json
+from pathlib import Path
+import unittest
+
+
+ROOT = Path(__file__).resolve().parents[1]
+EXPECTED_IMAGE = "ghcr.io/awsomefox/campfire:latest"
+EXPECTED_ICON = (
+ "https://raw.githubusercontent.com/AwsomeFox/campfire/main/"
+ "apps/web/public/pwa-512x512.png"
+)
+
+
+class CampfireProfileTests(unittest.TestCase):
+ def test_app_and_curated_profiles_match_the_single_service_contract(self):
+ for family in ("apps", "curated"):
+ with self.subTest(family=family):
+ template = json.loads(
+ (ROOT / "catalog" / family / "campfire.json")
+ .read_text(encoding="utf-8")
+ )
+
+ contract = template["container_contract"]
+ stack = template["compose_stack"]
+ self.assertEqual(contract["image"]["reference"], EXPECTED_IMAGE)
+ self.assertEqual(stack["service_count"], 1)
+ self.assertEqual(len(stack["services"]), 1)
+ service = stack["services"][0]
+ self.assertEqual(service["image"], EXPECTED_IMAGE)
+ self.assertEqual(service["compose"]["image"], EXPECTED_IMAGE)
+
+ volume_paths = {
+ volume["container_path"] for volume in contract["volumes"]
+ }
+ self.assertIn("/data", volume_paths)
+ self.assertIn("./data-0:/data", service["compose"]["volumes"])
+
+ setup = next(
+ endpoint for endpoint in template["first_run"]["endpoints"]
+ if endpoint["label"] == "Campfire first-run setup"
+ )
+ self.assertEqual(setup["path"], "/auth/setup")
+
+ healthcheck = template["proxmox"]["installer_profile"][
+ "startup_healthcheck"
+ ]
+ self.assertEqual(healthcheck["path"], "/readyz")
+
+ self.assertEqual(template["catalog_ui"]["icon"], EXPECTED_ICON)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/oci/tests/test_next10_b_profiles.py b/oci/tests/test_next10_b_profiles.py
new file mode 100644
index 00000000..dc769c3b
--- /dev/null
+++ b/oci/tests/test_next10_b_profiles.py
@@ -0,0 +1,156 @@
+"""Static install contracts for the next OCI catalog profiles."""
+
+import json
+from pathlib import Path
+import unittest
+
+
+ROOT = Path(__file__).resolve().parents[1]
+
+
+PROFILES = {
+ "chhoto-url": {
+ "image": "sintan1729/chhoto-url:latest",
+ "data_path": "/data",
+ "port": 4567,
+ "health_path": "/",
+ },
+ "openhabittracker": {
+ "image": "jinjinov/openhabittracker:latest",
+ "data_path": "/app/.OpenHabitTracker",
+ "port": 8080,
+ "health_path": "/login",
+ },
+ "watcharr": {
+ "image": "ghcr.io/sbondco/watcharr:latest",
+ "data_path": "/data",
+ "port": 3080,
+ "health_path": "/",
+ },
+ "ech0": {
+ "image": "sn0wl1n/ech0:latest",
+ "data_path": "/app/data",
+ "port": 6277,
+ "health_path": "/",
+ },
+ "restic-rest-server": {
+ "image": "restic/rest-server:latest",
+ "data_path": "/data",
+ "port": 8000,
+ "health_path": "/",
+ },
+}
+
+
+def load_profile(app_id):
+ path = ROOT / "catalog" / "apps" / f"{app_id}.json"
+ return json.loads(path.read_text(encoding="utf-8"))
+
+
+class Next10BProfiles(unittest.TestCase):
+ def test_each_profile_is_one_image_with_an_icon_and_persistent_data(self):
+ for app_id, expected in PROFILES.items():
+ with self.subTest(app=app_id):
+ profile = load_profile(app_id)
+ services = profile["compose_stack"]["services"]
+ image = profile["container_contract"]["image"]["reference"]
+
+ self.assertEqual(profile["compose_stack"]["service_count"], 1)
+ self.assertEqual(len(services), 1)
+ self.assertEqual(services[0]["image"], expected["image"])
+ self.assertEqual(image, expected["image"])
+ self.assertEqual(services[0]["depends_on"], [])
+
+ icon = profile["catalog_ui"].get("icon")
+ self.assertIsInstance(icon, str)
+ self.assertTrue(icon.startswith("https://"), icon)
+
+ mount_paths = {
+ volume["container_path"]
+ for volume in profile["container_contract"]["volumes"]
+ }
+ self.assertIn(expected["data_path"], mount_paths)
+
+ def test_each_profile_has_a_matching_first_start_check(self):
+ for app_id, expected in PROFILES.items():
+ with self.subTest(app=app_id):
+ profile = load_profile(app_id)
+ check = profile["proxmox"]["installer_profile"]["startup_healthcheck"]
+
+ self.assertEqual(check["scheme"], "http")
+ self.assertEqual(check["port"], expected["port"])
+ self.assertEqual(check["path"], expected["health_path"])
+ self.assertTrue(check["accept_any_status"])
+
+ def test_chhoto_url_uses_generated_password_and_data_database(self):
+ profile = load_profile("chhoto-url")
+ environment = {
+ item["name"]: item
+ for item in profile["container_contract"]["environment"]
+ }
+ generated = profile["proxmox"]["installer_profile"]["generated_sensitive_environment"]
+ credential = profile["first_run"]["credentials"][0]
+
+ self.assertIn("CHHOTO_PASSWORD", environment)
+ self.assertTrue(environment["CHHOTO_PASSWORD"]["sensitive"])
+ self.assertIn("CHHOTO_PASSWORD", generated)
+ self.assertEqual(credential["password_environment"], "CHHOTO_PASSWORD")
+ self.assertEqual(environment["CHHOTO_DB_URL"]["example"], "/data/urls.sqlite")
+ self.assertEqual(environment["CHHOTO_SQLITE_USE_WAL_MODE"]["example"], "True")
+
+ def test_openhabittracker_prompts_for_user_and_generates_password_and_jwt(self):
+ profile = load_profile("openhabittracker")
+ environment = {
+ item["name"]: item
+ for item in profile["container_contract"]["environment"]
+ }
+ generated = profile["proxmox"]["installer_profile"]["generated_sensitive_environment"]
+ credential = profile["first_run"]["credentials"][0]
+
+ self.assertIn("prompt", environment["AppSettings__UserName"])
+ self.assertIn("prompt", environment["AppSettings__Email"])
+ self.assertIn("AppSettings__Password", generated)
+ self.assertIn("AppSettings__JwtSecret", generated)
+ self.assertEqual(credential["username_environment"], "AppSettings__UserName")
+ self.assertEqual(credential["password_environment"], "AppSettings__Password")
+
+ def test_watcharr_first_registered_user_becomes_admin(self):
+ profile = load_profile("watcharr")
+ credentials = profile["first_run"]["credentials"]
+
+ self.assertEqual(profile["container_contract"]["environment"], [])
+ self.assertEqual(len(credentials), 1)
+ self.assertIn("administrator", credentials[0]["label"].lower())
+ self.assertIn("first-run setup", credentials[0]["username"].lower())
+
+ def test_ech0_generates_jwt_and_marks_first_user_as_owner(self):
+ profile = load_profile("ech0")
+ generated = profile["proxmox"]["installer_profile"]["generated_sensitive_environment"]
+ credentials = profile["first_run"]["credentials"]
+
+ self.assertIn("JWT_SECRET", generated)
+ self.assertEqual(profile["container_contract"]["environment"][0]["name"], "JWT_SECRET")
+ self.assertEqual(len(credentials), 1)
+ self.assertIn("Owner", credentials[0]["label"])
+ self.assertIn("first-run setup", credentials[0]["username"].lower())
+
+ def test_restic_server_is_an_api_without_a_browser_endpoint(self):
+ profile = load_profile("restic-rest-server")
+ credentials = profile["first_run"]["credentials"]
+ ui = profile["catalog_ui"]
+
+ self.assertEqual(profile["first_run"]["endpoints"], [])
+ self.assertIsNone(ui["launch"]["port"])
+ self.assertIn("no web interface", ui["tagline"]["en_US"].lower())
+ self.assertIn("not a browser-based application", ui["description"]["en_US"].lower())
+ self.assertEqual(len(credentials), 1)
+ self.assertIn("create_user", credentials[0]["username"])
+ self.assertIsNone(credentials[0]["password"])
+ self.assertEqual(
+ profile["container_contract"]["environment"][0]["example"],
+ "--private-repos",
+ )
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/oci/tests/test_next10_c_profiles.py b/oci/tests/test_next10_c_profiles.py
new file mode 100644
index 00000000..defc6b3b
--- /dev/null
+++ b/oci/tests/test_next10_c_profiles.py
@@ -0,0 +1,176 @@
+"""Guard the Activepieces, Bytebase, Metabase and Kopia OCI recipes."""
+import json
+from pathlib import Path
+import unittest
+
+
+ROOT = Path(__file__).resolve().parents[1]
+
+EXPECTED = {
+ "activepieces": {
+ "image": "ghcr.io/activepieces/activepieces:latest",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/activepieces.webp",
+ "health": (80, "/api/v1/health"),
+ "note_terms": ("personal", "memory"),
+ },
+ "bytebase": {
+ "image": "bytebase/bytebase:latest",
+ "icon": "https://cdn.jsdelivr.net/gh/bytebase/bytebase@main/frontend/src/assets/logo-icon.svg",
+ "health": (8080, "/healthz"),
+ "note_terms": (),
+ },
+ "metabase": {
+ "image": "metabase/metabase:v0.64-lts",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/metabase.webp",
+ "health": (3000, "/api/health"),
+ "note_terms": ("h2", "postgres"),
+ },
+ "kopia": {
+ "image": "kopia/kopia:latest",
+ "icon": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kopia.webp",
+ "health": (51515, "/"),
+ "note_terms": ("repository", "password"),
+ },
+}
+
+
+def read_profile(family, app):
+ path = ROOT / "catalog" / family / f"{app}.json"
+ return json.loads(path.read_text(encoding="utf-8"))
+
+
+class Next10CProfileTests(unittest.TestCase):
+ def test_image_single_service_and_icon_match_catalog_contract(self):
+ for app, expected in EXPECTED.items():
+ with self.subTest(app=app):
+ for family in ("apps", "curated"):
+ profile = read_profile(family, app)
+ stack = profile["compose_stack"]
+ service = stack["services"][0]
+ self.assertEqual(
+ profile["container_contract"]["image"]["reference"],
+ expected["image"],
+ )
+ self.assertEqual(stack["service_count"], 1)
+ self.assertEqual(len(stack["services"]), 1)
+ self.assertEqual(service["image"], expected["image"])
+ self.assertEqual(service["compose"]["image"], expected["image"])
+ self.assertEqual(profile["catalog_ui"]["icon"], expected["icon"])
+ self.assertTrue(expected["icon"].startswith("https://"))
+
+ def test_startup_healthcheck_targets_the_documented_service_endpoint(self):
+ for app, expected in EXPECTED.items():
+ with self.subTest(app=app):
+ for family in ("apps", "curated"):
+ profile = read_profile(family, app)
+ health = profile["proxmox"]["installer_profile"][
+ "startup_healthcheck"
+ ]
+ self.assertEqual(health["port"], expected["health"][0])
+ self.assertEqual(health["path"], expected["health"][1])
+ self.assertEqual(health["scheme"], "http")
+
+ def test_required_persistent_paths_are_mounted_by_the_single_service(self):
+ for app in EXPECTED:
+ with self.subTest(app=app):
+ for family in ("apps", "curated"):
+ profile = read_profile(family, app)
+ contract_paths = {
+ volume["container_path"]
+ for volume in profile["container_contract"]["volumes"]
+ if volume.get("required", False)
+ }
+ mounts = profile["compose_stack"]["services"][0]["compose"].get(
+ "volumes", []
+ )
+ mounted_paths = {
+ mount.split(":")[1]
+ for mount in mounts
+ if len(mount.split(":")) >= 2
+ }
+ self.assertTrue(contract_paths, "profile must declare persistent data")
+ self.assertTrue(
+ contract_paths.issubset(mounted_paths),
+ f"required paths missing from Compose mounts: "
+ f"{sorted(contract_paths - mounted_paths)}",
+ )
+
+ def test_secret_environment_is_sensitive_generated_and_wired_to_compose(self):
+ for app in EXPECTED:
+ with self.subTest(app=app):
+ for family in ("apps", "curated"):
+ profile = read_profile(family, app)
+ declared_sensitive = {
+ item["name"]
+ for item in profile["container_contract"].get("environment", [])
+ if item.get("sensitive")
+ }
+ runtime_generated = profile["proxmox"]["installer_profile"].get(
+ "generated_sensitive_environment", {}
+ )
+ generated_bindings = {
+ binding["environment_variable"]
+ for secret in profile["compose_stack"]["installer_inputs"].get(
+ "generated_secrets", []
+ )
+ for binding in secret.get("bindings", [])
+ }
+ compose_environment = profile["compose_stack"]["services"][0][
+ "compose"
+ ].get("environment", {})
+
+ if app == "bytebase":
+ self.assertEqual(generated_bindings, set())
+ self.assertEqual(runtime_generated, {})
+ self.assertNotIn("HBOX_AUTH_API_KEY_PEPPER", declared_sensitive)
+ self.assertNotIn("HBOX_AUTH_API_KEY_PEPPER", compose_environment)
+ self.assertNotIn("homebox", json.dumps(profile).casefold())
+ continue
+
+ self.assertTrue(
+ generated_bindings,
+ "profile must document generated credentials/secrets",
+ )
+ self.assertEqual(
+ generated_bindings,
+ set(runtime_generated),
+ "generated secret bindings and runtime generators must match",
+ )
+ self.assertTrue(
+ generated_bindings.issubset(declared_sensitive),
+ "every generated secret must be declared sensitive in the contract",
+ )
+ for variable in generated_bindings:
+ self.assertIn(variable, compose_environment)
+ self.assertIn(variable, str(compose_environment[variable]))
+
+ def test_app_notes_explain_the_key_limitations_or_setup_requirements(self):
+ for app, expected in EXPECTED.items():
+ with self.subTest(app=app):
+ for family in ("apps", "curated"):
+ profile = read_profile(family, app)
+ if app == "bytebase":
+ endpoints = profile["first_run"]["endpoints"]
+ web_ui = next(
+ endpoint for endpoint in endpoints
+ if endpoint["label"] == "Web UI"
+ )
+ self.assertEqual(web_ui["scheme"], "http")
+ self.assertEqual(web_ui["port"], 8080)
+ self.assertEqual(web_ui["path"], "/")
+ self.assertEqual(profile["first_run"]["credentials"], [])
+ self.assertEqual(
+ profile["catalog_ui"]["documentation"],
+ "https://docs.bytebase.com/get-started/self-host/deploy-with-docker",
+ )
+ continue
+
+ tips = profile["catalog_ui"].get("tips", [])
+ note = " ".join(tips).casefold()
+ self.assertTrue(tips, "profile must include app-specific notes")
+ for term in expected["note_terms"]:
+ self.assertIn(term, note)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/scripts/global/log2ram_keep_on_disk.sh b/scripts/global/log2ram_keep_on_disk.sh
new file mode 100755
index 00000000..bbffe243
--- /dev/null
+++ b/scripts/global/log2ram_keep_on_disk.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+# ==========================================================
+# ProxMenux - Folders of /var/log that Log2RAM leaves on disk
+# ==========================================================
+# Runs before Log2RAM puts /var/log in RAM. The journal, the task logs of
+# Proxmox VE and those of Proxmox Backup Server are records a host needs after
+# a power cut, and a task cannot run if its log cannot be written, so they
+# stay on disk: each folder moves to
+# /var/hdd. and its usual place becomes a link to it. Log2RAM copies a
+# link as a link, so their contents never enter the RAM.
+#
+# With PMX_L2R_REVERT=1 it puts the folders back in /var/log and removes the
+# links; the uninstaller runs it that way at the next boot.
+#
+# PMX_L2R_ROOT prefixes every path, for tests.
+# ==========================================================
+
+ROOT="${PMX_L2R_ROOT:-}"
+LOG_DIR="$ROOT/var/log"
+
+# Only while /var/log is the folder on disk, never the copy in RAM.
+if [ -z "$ROOT" ] && mountpoint -q /var/log; then
+ exit 0
+fi
+
+KEPT_ON_DISK="journal pve proxmox-backup"
+
+keep_on_disk() {
+ local name="$1"
+ local place="$LOG_DIR/$name" disk="$ROOT/var/hdd.$name"
+
+ if [ -L "$place" ]; then
+ [ -d "$disk" ] || mkdir -p "$disk"
+ return 0
+ fi
+ # The journal is always kept on disk; any other folder only where it exists.
+ if [ ! -d "$place" ]; then
+ [ "$name" = journal ] || return 0
+ mkdir -p "$disk"
+ chown root:systemd-journal "$disk" 2>/dev/null || true
+ chmod 2755 "$disk"
+ elif [ ! -e "$disk" ]; then
+ mv "$place" "$disk" || return 1
+ else
+ cp -a "$place/." "$disk/" || return 1
+ rm -rf "${place:?}"
+ fi
+ ln -s "/var/hdd.$name" "$place"
+}
+
+put_back() {
+ local name="$1"
+ local place="$LOG_DIR/$name" disk="$ROOT/var/hdd.$name"
+
+ [ -d "$disk" ] || { [ -L "$place" ] && rm -f "$place"; return 0; }
+ [ -L "$place" ] && rm -f "$place"
+ if [ ! -e "$place" ]; then
+ mv "$disk" "$place" || return 1
+ else
+ cp -a "$disk/." "$place/" || return 1
+ rm -rf "${disk:?}"
+ fi
+}
+
+status=0
+for name in $KEPT_ON_DISK; do
+ if [ "${PMX_L2R_REVERT:-0}" = 1 ]; then
+ put_back "$name" || status=1
+ else
+ keep_on_disk "$name" || status=1
+ fi
+done
+exit "$status"
diff --git a/scripts/global/log2ram_setup.sh b/scripts/global/log2ram_setup.sh
new file mode 100644
index 00000000..02f5abff
--- /dev/null
+++ b/scripts/global/log2ram_setup.sh
@@ -0,0 +1,214 @@
+#!/bin/bash
+# ==========================================================
+# ProxMenux - Log2RAM setup shared by both post-install modes
+# ==========================================================
+# Log2RAM keeps /var/log in RAM and writes it to disk on a schedule. The
+# journal and the task logs of Proxmox VE and Proxmox Backup Server stay on
+# disk: see log2ram_keep_on_disk.sh, which moves them before Log2RAM starts.
+#
+# Requires utils.sh (translate, msg_*) and pmx_journal.sh (pmx_write_file...).
+# ==========================================================
+
+PMX_L2R_TAG="1.7.2"
+PMX_L2R_COMMIT="f171449530f8984374c8f69fff3c92af813a694b"
+PMX_L2R_REPO="https://github.com/azlux/log2ram.git"
+PMX_L2R_SCRIPTS="/usr/local/share/proxmenux/scripts/global"
+PMX_L2R_REVERT_UNIT="/etc/systemd/system/proxmenux-log2ram-revert.service"
+
+# Size in MB and sync interval in hours proposed for this host's RAM.
+pmx_log2ram_default_size() {
+ local ram_gb
+ ram_gb="$(awk '/^MemTotal:/{print int($2/1024/1024)}' /proc/meminfo)"
+ if (( ${ram_gb:-4} <= 8 )); then echo 128
+ elif (( ram_gb <= 16 )); then echo 256
+ else echo 512; fi
+}
+
+pmx_log2ram_default_hours() {
+ local ram_gb
+ ram_gb="$(awk '/^MemTotal:/{print int($2/1024/1024)}' /proc/meminfo)"
+ if (( ${ram_gb:-4} <= 8 )); then echo 1
+ elif (( ram_gb <= 16 )); then echo 3
+ else echo 6; fi
+}
+
+# What an installed Log2RAM is set to, for an update to keep it.
+pmx_log2ram_current_size() {
+ local size
+ size="$(grep -E '^SIZE=' /etc/log2ram.conf 2>/dev/null | tail -1 | cut -d= -f2 | tr -dc '0-9')"
+ echo "${size:-$(pmx_log2ram_default_size)}"
+}
+
+pmx_log2ram_current_hours() {
+ local hours
+ hours="$(sed -n 's/^OnCalendar=\*-\*-\* 00\/\([0-9]*\):55:00$/\1/p' \
+ /etc/systemd/system/log2ram-daily.timer.d/proxmenux.conf 2>/dev/null)"
+ # Earlier versions synced from a cron entry: "0 */N * * * root ... write".
+ [[ -n "$hours" ]] || hours="$(sed -n 's/^0 \*\/\([0-9]*\) .*log2ram write.*/\1/p' /etc/cron.d/log2ram 2>/dev/null)"
+ echo "${hours:-$(pmx_log2ram_default_hours)}"
+}
+
+# Size-based rotation for the API logs of Proxmox Backup Server, whose
+# package ships no rule for them.
+_pmx_log2ram_pbs_logrotate() {
+ dpkg-query -W -f='${Status}' proxmox-backup-server 2>/dev/null | grep -q 'install ok installed' || return 0
+ pmx_write_file /etc/logrotate.d/proxmox-backup-api <<'EOF'
+/var/log/proxmox-backup/api/access.log /var/log/proxmox-backup/api/auth.log {
+ size 20M
+ rotate 3
+ compress
+ missingok
+ notifempty
+ copytruncate
+}
+EOF
+ chmod 0644 /etc/logrotate.d/proxmox-backup-api
+}
+
+# Files of the Log2RAM release, copied from a verified checkout. Its own
+# install.sh is not run: it refuses to run while Log2RAM is active and starts
+# by deleting /var/hdd.log, which is the disk copy of /var/log while mounted.
+_pmx_log2ram_install_release() {
+ local checkout="/tmp/log2ram-${PMX_L2R_TAG}"
+ rm -rf "$checkout"
+ if ! git clone --quiet --depth 1 --branch "$PMX_L2R_TAG" "$PMX_L2R_REPO" "$checkout" >/dev/null 2>&1; then
+ msg_error "$(translate "Log2RAM could not be downloaded")"
+ return 1
+ fi
+ if [[ "$(git -C "$checkout" rev-parse HEAD 2>/dev/null)" != "$PMX_L2R_COMMIT" ]]; then
+ rm -rf "$checkout"
+ msg_error "$(translate "The downloaded Log2RAM does not match the expected release")"
+ return 1
+ fi
+ mkdir -p /usr/local/bin
+ install -m 644 "$checkout/log2ram.service" /etc/systemd/system/log2ram.service
+ install -m 644 "$checkout/log2ram-daily.service" /etc/systemd/system/log2ram-daily.service
+ install -m 644 "$checkout/log2ram-daily.timer" /etc/systemd/system/log2ram-daily.timer
+ install -m 755 "$checkout/log2ram" /usr/local/bin/log2ram
+ [[ -d /etc/logrotate.d ]] && install -m 644 "$checkout/log2ram.logrotate" /etc/logrotate.d/log2ram
+ [[ -f /etc/log2ram.conf ]] || install -m 644 "$checkout/log2ram.conf" /etc/log2ram.conf
+ rm -rf "$checkout"
+}
+
+# What earlier versions of this function installed and this one replaces.
+_pmx_log2ram_remove_legacy() {
+ local path
+ for path in /etc/cron.d/log2ram /usr/local/bin/log2ram-check.sh \
+ /etc/cron.hourly/proxmox-backup-logrotate \
+ /usr/local/bin/log2ram.proxmenux.bak /usr/sbin/log2ram.proxmenux.bak; do
+ pmx_remove_file "$path" 2>/dev/null || true
+ done
+}
+
+# pmx_log2ram_setup SIZE_MB SYNC_HOURS WATCH(true|false)
+pmx_log2ram_setup() {
+ local size_mb="$1" hours="$2" watch="$3"
+ local keep_free_mb=$(( size_mb / 2 ))
+
+ command -v rsync >/dev/null 2>&1 || pmx_install_pkg rsync
+ command -v git >/dev/null 2>&1 || pmx_install_pkg git
+
+ msg_info "$(translate "Installing Log2RAM") ${PMX_L2R_TAG}..."
+ _pmx_log2ram_install_release || return 1
+ msg_ok "$(translate "Log2RAM installed:") ${PMX_L2R_TAG}"
+
+ # The journal is not in RAM, so Log2RAM does not rotate it before a sync;
+ # and Proxmox has no mail command for its error notice.
+ pmx_edit_file /etc/log2ram.conf \
+ -e "s/^SIZE=.*/SIZE=${size_mb}M/" \
+ -e "s/^JOURNALD_AWARE=.*/JOURNALD_AWARE=false/" \
+ -e "s/^#\?NOTIFICATION=.*/NOTIFICATION=false/"
+ grep -q '^NOTIFICATION=' /etc/log2ram.conf || printf 'NOTIFICATION=false\n' | pmx_append_file /etc/log2ram.conf
+
+ mkdir -p /etc/systemd/system/log2ram.service.d /etc/systemd/system/log2ram-daily.timer.d
+ pmx_write_file /etc/systemd/system/log2ram.service.d/proxmenux.conf </dev/null 2>&1
+EOF
+ chmod 0644 /etc/cron.d/log2ram-auto-sync
+ else
+ pmx_remove_file /etc/cron.d/log2ram-auto-sync 2>/dev/null || true
+ fi
+
+ _pmx_log2ram_pbs_logrotate
+ _pmx_log2ram_remove_legacy
+ pmx_remove_file "$PMX_L2R_REVERT_UNIT" 2>/dev/null || true
+
+ systemctl daemon-reload >/dev/null 2>&1 || true
+ pmx_apply_setting "service-enabled:log2ram" "systemctl is-enabled log2ram" \
+ systemctl enable log2ram.service log2ram-daily.timer || return 1
+ msg_ok "$(translate "Log2RAM keeps /var/log in RAM; the journal stays on disk from the next boot")"
+}
+
+# Log2RAM stops at the next shutdown, writing /var/log to disk on its way;
+# the boot after that puts the journal back in /var/log.
+pmx_log2ram_remove() {
+ pmx_disable_service log2ram-daily.timer 2>/dev/null || true
+ systemctl disable log2ram.service >/dev/null 2>&1 || true
+
+ local path
+ for path in /etc/cron.d/log2ram-auto-sync /etc/systemd/journald.conf.d/proxmenux-log2ram.conf \
+ /etc/tmpfiles.d/journal-nocow.conf /etc/systemd/system/log2ram.service.d/proxmenux.conf \
+ /etc/systemd/system/log2ram-daily.timer.d/proxmenux.conf; do
+ pmx_remove_file "$path" 2>/dev/null || true
+ done
+ _pmx_log2ram_remove_legacy
+
+ pmx_write_file "$PMX_L2R_REVERT_UNIT" </dev/null; rm -f /usr/local/bin/log2ram /usr/local/bin/uninstall-log2ram.sh /etc/log2ram.conf /etc/logrotate.d/log2ram /etc/systemd/system/log2ram.service /etc/systemd/system/log2ram-daily.service /etc/systemd/system/log2ram-daily.timer; rmdir /etc/systemd/system/log2ram.service.d /etc/systemd/system/log2ram-daily.timer.d 2>/dev/null; systemctl disable proxmenux-log2ram-revert.service; rm -f ${PMX_L2R_REVERT_UNIT}; systemctl daemon-reload'
+
+[Install]
+WantedBy=sysinit.target
+EOF
+ systemctl daemon-reload >/dev/null 2>&1 || true
+ systemctl enable proxmenux-log2ram-revert.service >/dev/null 2>&1 || true
+}
diff --git a/scripts/global/log2ram_watch.sh b/scripts/global/log2ram_watch.sh
new file mode 100755
index 00000000..45b889db
--- /dev/null
+++ b/scripts/global/log2ram_watch.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+# ==========================================================
+# ProxMenux - Keeps the Log2RAM folder from filling up
+# ==========================================================
+# Past 85% of its size, the logs in RAM are rotated and the ones just rotated
+# are compressed at once, then everything is written to disk. Rules with
+# delaycompress, such as the one of pveproxy, would otherwise leave the largest
+# log uncompressed until the next day. No log is emptied.
+# ==========================================================
+
+PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
+
+mountpoint -q /var/log || exit 0
+used="$(df --output=pcent /var/log 2>/dev/null | tail -1 | tr -dc '0-9')"
+[ "${used:-0}" -ge 85 ] || exit 0
+
+exec 9>/run/proxmenux-log2ram-watch.lock || exit 0
+flock -n 9 || exit 0
+
+logrotate -f /etc/logrotate.conf >/dev/null 2>&1 || true
+find /var/log -xdev -type f -name '*.1' -exec gzip -9 -- {} + 2>/dev/null || true
+systemctl reload log2ram >/dev/null 2>&1 || true
diff --git a/scripts/global/remove-banner-pve-v3.sh b/scripts/global/remove-banner-pve-v3.sh
index 76df07b5..24c1fa30 100644
--- a/scripts/global/remove-banner-pve-v3.sh
+++ b/scripts/global/remove-banner-pve-v3.sh
@@ -287,6 +287,7 @@ reload_services() {
systemctl is-active --quiet proxmox-backup-proxy 2>/dev/null && {
systemctl reload proxmox-backup-proxy 2>/dev/null || systemctl restart proxmox-backup-proxy 2>/dev/null || true
}
+ return 0
}
main() {
diff --git a/scripts/global/update-pve-safe.sh b/scripts/global/update-pve-safe.sh
index 5fd99fdc..c9056fdd 100644
--- a/scripts/global/update-pve-safe.sh
+++ b/scripts/global/update-pve-safe.sh
@@ -205,11 +205,12 @@ update_pve_safe() {
fi
# The package list rides in the same dialog as the summary, so the
- # decision is taken knowing what is about to be replaced. --scrolltext
- # keeps the buttons reachable however long the list is.
+ # decision is taken knowing what is about to be replaced. Keep Yes focused
+ # while the list remains scrollable; --keep-tite restores the execution screen.
menu_text+="$(translate "Packages to be upgraded"):\n$upgradable_list\n\n"
menu_text+="$(translate "Do you want to proceed with the system update?")"
- if ! whiptail --title "$(translate "Proxmox Update")" --scrolltext --yesno "$menu_text" 24 78; then
+ if ! dialog --keep-tite --title "$(translate "Proxmox Update")" \
+ --default-button yes --no-collapse --scrollbar --yesno "$menu_text" 24 78; then
msg_info2 "$(translate "Update cancelled by user")"
apt-get -y autoremove >/dev/null 2>&1 || true
apt-get -y autoclean >/dev/null 2>&1 || true
diff --git a/scripts/global/update-pve8.sh b/scripts/global/update-pve8.sh
index 34f36267..01503294 100644
--- a/scripts/global/update-pve8.sh
+++ b/scripts/global/update-pve8.sh
@@ -169,10 +169,12 @@ EOF
else
# The package list rides in the same dialog as the summary, so
# the decision is taken knowing what is about to be replaced.
- # --scrolltext keeps the buttons reachable however long it is.
+ # Keep Yes focused while the full package list remains scrollable.
+ # --keep-tite restores the execution screen after confirmation.
menu_text+="$(translate "Packages to be upgraded"):\n$upgradable_list\n\n"
menu_text+="$(translate "Do you want to proceed with the system update?")"
- if whiptail --title "$(translate "Proxmox Update")" --scrolltext --yesno "$menu_text" 24 78; then
+ if dialog --keep-tite --title "$(translate "Proxmox Update")" \
+ --default-button yes --no-collapse --scrollbar --yesno "$menu_text" 24 78; then
return 0
else
return 1
diff --git a/scripts/global/update-pve9_2.sh b/scripts/global/update-pve9_2.sh
index 03773e06..6f01caff 100644
--- a/scripts/global/update-pve9_2.sh
+++ b/scripts/global/update-pve9_2.sh
@@ -272,10 +272,12 @@ EOF
else
# The package list rides in the same dialog as the summary, so
# the decision is taken knowing what is about to be replaced.
- # --scrolltext keeps the buttons reachable however long it is.
+ # Keep Yes focused while the full package list remains scrollable.
+ # --keep-tite restores the execution screen after confirmation.
menu_text+="$(translate "Packages to be upgraded"):\n$upgradable_list\n\n"
menu_text+="$(translate "Do you want to proceed with the system update?")"
- if whiptail --title "$(translate "Proxmox Update")" --scrolltext --yesno "$menu_text" 24 78; then
+ if dialog --keep-tite --title "$(translate "Proxmox Update")" \
+ --default-button yes --no-collapse --scrollbar --yesno "$menu_text" 24 78; then
return 0
else
return 1
diff --git a/scripts/global/utils-install-functions.sh b/scripts/global/utils-install-functions.sh
index e03760be..c4a79559 100644
--- a/scripts/global/utils-install-functions.sh
+++ b/scripts/global/utils-install-functions.sh
@@ -401,18 +401,3 @@ pmx_rebuild_dkms_after_kernel() {
fi
return 0
}
-
-# Log2RAM syncs only the archived journal files, so the active file it found
-# on disk when it was installed stays there. Restored at every boot, it makes
-# journald continue an old sequence and delete its newest archives first.
-# The sync removes that file from the disk copy. Returns 0 when it changes
-# the given Log2RAM script.
-pmx_log2ram_drop_stale_journal() {
- local log2ram_bin="$1"
- [[ -f "$log2ram_bin" ]] || return 1
- grep -q -- '--exclude=journal/\*/\*' "$log2ram_bin" 2>/dev/null || return 1
- grep -q -- '--delete-excluded' "$log2ram_bin" 2>/dev/null && return 1
- [[ -e "${log2ram_bin}.proxmenux.bak" ]] || cp -a "$log2ram_bin" "${log2ram_bin}.proxmenux.bak"
- sed '/optional_params+=("--exclude=journal\/\*\/\*")/a\ optional_params+=("--delete-excluded")' "$log2ram_bin" \
- | pmx_write_file "$log2ram_bin"
-}
diff --git a/scripts/post_install/auto_post_install.sh b/scripts/post_install/auto_post_install.sh
index 052b7393..2dbc4992 100644
--- a/scripts/post_install/auto_post_install.sh
+++ b/scripts/post_install/auto_post_install.sh
@@ -53,6 +53,9 @@ fi
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
fi
+if [[ -f "$LOCAL_SCRIPTS/global/log2ram_setup.sh" ]]; then
+ source "$LOCAL_SCRIPTS/global/log2ram_setup.sh"
+fi
load_language
initialize_cache
@@ -959,148 +962,18 @@ EOF
_update_existing_log2ram_auto() {
local func_version="$1"
- local log2ram_bin=""
- local candidate resolved tmp_file
pmx_journal_context "_update_existing_log2ram_auto" "$func_version"
msg_ok "$(translate "Log2RAM already registered — updating to latest configuration")"
-
- for candidate in \
- "$(command -v log2ram 2>/dev/null)" \
- /usr/local/bin/log2ram \
- /usr/sbin/log2ram \
- /usr/bin/log2ram
- do
- [[ -n "$candidate" && -f "$candidate" ]] || continue
- resolved="$(readlink -f "$candidate" 2>/dev/null || printf '%s' "$candidate")"
- [[ -f "$resolved" ]] || continue
- log2ram_bin="$resolved"
- break
- done
-
- if [[ -z "$log2ram_bin" || ! -f /etc/log2ram.conf ]]; then
- msg_error "$(translate "Log2RAM installation verification failed. Check /tmp/log2ram_install.log")"
- return 1
- fi
-
- if grep -q 'rsync -aAXv ' "$log2ram_bin" 2>/dev/null; then
- [[ -e "${log2ram_bin}.proxmenux.bak" ]] || cp -a "$log2ram_bin" "${log2ram_bin}.proxmenux.bak"
- sed 's/rsync -aAXv /rsync -aXv --no-acls /g' "$log2ram_bin" | pmx_write_file "$log2ram_bin"
- fi
-
- if pmx_log2ram_drop_stale_journal "$log2ram_bin"; then
- NECESSARY_REBOOT=1
- msg_ok "$(translate "Log2RAM keeps the journal in order from the next reboot")"
- fi
-
- if dpkg-query -W -f='${Status}' proxmox-backup-server 2>/dev/null \
- | grep -q 'install ok installed'; then
- tmp_file="$(mktemp /etc/logrotate.d/.proxmox-backup-api.XXXXXX)" || return 1
- cat > "$tmp_file" <<'EOF'
-/var/log/proxmox-backup/api/access.log /var/log/proxmox-backup/api/auth.log {
- size 20M
- rotate 3
- compress
- missingok
- notifempty
- copytruncate
-}
-EOF
- chmod 0644 "$tmp_file"
- chown root:root "$tmp_file"
- pmx_write_file /etc/logrotate.d/proxmox-backup-api < "$tmp_file"
- rm -f "$tmp_file"
-
- tmp_file="$(mktemp /etc/cron.hourly/.proxmox-backup-logrotate.XXXXXX)" || return 1
- cat > "$tmp_file" <<'EOF'
-#!/bin/sh
-/usr/sbin/logrotate /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1
-EOF
- chmod 0755 "$tmp_file"
- chown root:root "$tmp_file"
- pmx_write_file /etc/cron.hourly/proxmox-backup-logrotate < "$tmp_file"
- chmod 0755 /etc/cron.hourly/proxmox-backup-logrotate
- chown root:root /etc/cron.hourly/proxmox-backup-logrotate
- rm -f "$tmp_file"
- msg_ok "$(translate "PBS API log rotation configured (hourly, size-based)")"
- fi
-
- tmp_file="$(mktemp /usr/local/bin/.log2ram-check.XXXXXX)" || return 1
- cat > "$tmp_file" <<'EOF'
-#!/usr/bin/env bash
-# Watch /var/log usage on Log2RAM's tmpfs and act at two thresholds:
-# > 80% → vacuum journald down to ~30% of SIZE, then log2ram write
-# > 92% → aggressive: journal to ~5%, rotate PBS API logs if present,
-# truncate existing pveproxy/pveam logs, then log2ram write
-PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
-
-CONF_FILE="/etc/log2ram.conf"
-L2R_BIN="$(command -v log2ram || true)"
-[[ -z "$L2R_BIN" && -x /usr/sbin/log2ram ]] && L2R_BIN="/usr/sbin/log2ram"
-[[ -z "$L2R_BIN" ]] && exit 0
-
-SIZE_MiB="$(grep -E '^SIZE=' "$CONF_FILE" 2>/dev/null | cut -d'=' -f2 | tr -dc '0-9')"
-[[ -z "$SIZE_MiB" ]] && SIZE_MiB=128
-LIMIT_BYTES=$(( SIZE_MiB * 1024 * 1024 ))
-WARN_BYTES=$(( LIMIT_BYTES * 80 / 100 ))
-EMERGENCY_BYTES=$(( LIMIT_BYTES * 92 / 100 ))
-
-USED_BYTES="$(df -B1 --output=used /var/log 2>/dev/null | tail -1 | tr -dc '0-9')"
-[[ -z "$USED_BYTES" ]] && exit 0
-
-LOCK="/run/log2ram-check.lock"
-exec 9>"$LOCK" 2>/dev/null || exit 0
-flock -n 9 || exit 0
-
-if (( USED_BYTES > EMERGENCY_BYTES )); then
- SAFE_JOURNAL_MB=$(( SIZE_MiB * 5 / 100 ))
- [[ "$SAFE_JOURNAL_MB" -lt 16 ]] && SAFE_JOURNAL_MB=16
- journalctl --vacuum-size="${SAFE_JOURNAL_MB}M" >/dev/null 2>&1 || true
- if [[ -x /usr/sbin/logrotate && -f /etc/logrotate.d/proxmox-backup-api ]]; then
- /usr/sbin/logrotate -f /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1 || true
- fi
- [ -e /var/log/pveproxy/access.log ] && : > /var/log/pveproxy/access.log 2>/dev/null || true
- [ -e /var/log/pveproxy/error.log ] && : > /var/log/pveproxy/error.log 2>/dev/null || true
- [ -e /var/log/pveam.log ] && : > /var/log/pveam.log 2>/dev/null || true
- "$L2R_BIN" write 2>/dev/null || true
-elif (( USED_BYTES > WARN_BYTES )); then
- SOFT_JOURNAL_MB=$(( SIZE_MiB * 30 / 100 ))
- [[ "$SOFT_JOURNAL_MB" -lt 32 ]] && SOFT_JOURNAL_MB=32
- journalctl --vacuum-size="${SOFT_JOURNAL_MB}M" >/dev/null 2>&1 || true
- "$L2R_BIN" write 2>/dev/null || true
-fi
-EOF
- chmod 0755 "$tmp_file"
- chown root:root "$tmp_file"
- bash -n "$tmp_file" || return 1
- pmx_write_file /usr/local/bin/log2ram-check.sh < "$tmp_file"
- chmod 0755 /usr/local/bin/log2ram-check.sh
- chown root:root /usr/local/bin/log2ram-check.sh
- rm -f "$tmp_file"
-
- tmp_file="$(mktemp /etc/cron.d/.log2ram-auto-sync.XXXXXX)" || return 1
- cat > "$tmp_file" <<'EOF'
-# Log2RAM auto-sync based on /var/log usage - Created by ProxMenux
-SHELL=/bin/bash
-PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
-MAILTO=""
-# nice/ionice keep the check off the priority queue for scheduled tasks.
-3-59/10 * * * * root nice -n 19 ionice -c 3 /usr/local/bin/log2ram-check.sh >/dev/null 2>&1
-EOF
- chmod 0644 "$tmp_file"
- chown root:root "$tmp_file"
- pmx_write_file /etc/cron.d/log2ram-auto-sync < "$tmp_file"
- chmod 0644 /etc/cron.d/log2ram-auto-sync
- chown root:root /etc/cron.d/log2ram-auto-sync
- rm -f "$tmp_file"
-
+ pmx_log2ram_setup "$(pmx_log2ram_current_size)" "$(pmx_log2ram_current_hours)" true || return 1
+ NECESSARY_REBOOT=1
register_tool "log2ram" true "$func_version"
msg_success "$(translate "Log2RAM installation and configuration completed successfully.")"
}
install_log2ram_auto() {
- local FUNC_VERSION="1.8"
+ local FUNC_VERSION="2.0"
local existing_log2ram_bin=""
pmx_journal_context "install_log2ram_auto" "$FUNC_VERSION"
@@ -1156,270 +1029,8 @@ install_log2ram_auto() {
fi # end first-time install block
- msg_info "$(translate "Cleaning previous Log2RAM installation...")"
-
- pmx_disable_service log2ram || true
- pmx_disable_service log2ram-daily.timer || true
-
- local obsolete_path
- for obsolete_path in \
- /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync \
- /etc/cron.hourly/log2ram /etc/cron.daily/log2ram \
- /etc/cron.weekly/log2ram /etc/cron.monthly/log2ram \
- /usr/local/bin/log2ram-check.sh /usr/local/bin/log2ram /usr/sbin/log2ram \
- /etc/systemd/system/log2ram.service \
- /etc/systemd/system/log2ram-daily.timer \
- /etc/systemd/system/log2ram-daily.service \
- /etc/systemd/system/sysinit.target.wants/log2ram.service \
- /etc/log2ram.conf /etc/log2ram.conf.* /etc/logrotate.d/log2ram
- do
- pmx_remove_file "$obsolete_path" 2>/dev/null || true
- done
- rm -rf /etc/systemd/system/log2ram.service.d 2>/dev/null || true
- rm -rf /var/log.hdd /tmp/log2ram 2>/dev/null || true
-
- systemctl daemon-reexec >/dev/null 2>&1 || true
- systemctl daemon-reload >/dev/null 2>&1 || true
- systemctl restart cron >/dev/null 2>&1 || true
-
- msg_ok "$(translate "Previous installation cleaned")"
- msg_info "$(translate "Installing Log2RAM from source...")"
-
- if ! command -v git >/dev/null 2>&1; then
- apt-get update -qq >/dev/null 2>&1
- pmx_install_pkg git
- fi
-
- rm -rf /tmp/log2ram 2>/dev/null || true
- if ! git clone --depth 1 https://github.com/azlux/log2ram.git /tmp/log2ram >/dev/null 2>>/tmp/log2ram_install.log; then
- msg_error "$(translate "Failed to clone log2ram repository. Check /tmp/log2ram_install.log")"
- return 1
- fi
-
- cd /tmp/log2ram || { msg_error "$(translate "Failed to access log2ram directory")"; return 1; }
-
- if ! bash install.sh >>/tmp/log2ram_install.log 2>&1; then
- msg_error "$(translate "Failed to run log2ram installer. Check /tmp/log2ram_install.log")"
- return 1
- fi
-
- # Drop ACL preservation from the upstream rsync call: some
- # /var/log.hdd filesystems reject POSIX ACLs and log2ram write
- # exits 23 with `set_acl: Operation not supported`. xattrs stay.
- local _l2r_bin
- for _l2r_bin in \
- "$(command -v log2ram 2>/dev/null)" \
- /usr/local/bin/log2ram \
- /usr/sbin/log2ram \
- /usr/bin/log2ram
- do
- [[ -n "$_l2r_bin" && -f "$_l2r_bin" ]] || continue
- if grep -q 'rsync -aAXv ' "$_l2r_bin" 2>/dev/null; then
- cp -a "$_l2r_bin" "${_l2r_bin}.proxmenux.bak"
- pmx_edit_file "$_l2r_bin" 's/rsync -aAXv /rsync -aXv --no-acls /g'
- fi
- pmx_log2ram_drop_stale_journal "$_l2r_bin" || true
- break
- done
-
- # Size-based rotation for the PBS API logs — the upstream package
- # ships no logrotate rule and pvestatd's local-datastore poll fills
- # them fast enough to saturate a tmpfs /var/log.
- if dpkg-query -W -f='${Status}' proxmox-backup-server 2>/dev/null \
- | grep -q 'install ok installed'; then
- mkdir -p /var/log/proxmox-backup/api 2>/dev/null || true
- pmx_write_file /etc/logrotate.d/proxmox-backup-api <<'EOF'
-/var/log/proxmox-backup/api/access.log /var/log/proxmox-backup/api/auth.log {
- size 20M
- rotate 3
- compress
- missingok
- notifempty
- copytruncate
-}
-EOF
- chmod 0644 /etc/logrotate.d/proxmox-backup-api
- chown root:root /etc/logrotate.d/proxmox-backup-api
- pmx_write_file /etc/cron.hourly/proxmox-backup-logrotate <<'EOF'
-#!/bin/sh
-/usr/sbin/logrotate /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1
-EOF
- chmod 0755 /etc/cron.hourly/proxmox-backup-logrotate
- chown root:root /etc/cron.hourly/proxmox-backup-logrotate
- msg_ok "$(translate "PBS API log rotation configured (hourly, size-based)")"
- fi
-
- systemctl daemon-reload >/dev/null 2>&1 || true
-
- if [[ -f /etc/log2ram.conf ]] && command -v log2ram >/dev/null 2>&1; then
- msg_ok "$(translate "Log2RAM installed successfully")"
- else
- msg_error "$(translate "Log2RAM installation verification failed. Check /tmp/log2ram_install.log")"
- return 1
- fi
-
- RAM_SIZE_GB=$(awk '/^MemTotal:/{print int($2/1024/1024)}' /proc/meminfo)
- [[ -z "$RAM_SIZE_GB" || "$RAM_SIZE_GB" -eq 0 ]] && RAM_SIZE_GB=4
-
- if (( RAM_SIZE_GB <= 8 )); then
- LOG2RAM_SIZE="128M"; CRON_HOURS=1
- elif (( RAM_SIZE_GB <= 16 )); then
- LOG2RAM_SIZE="256M"; CRON_HOURS=3
- else
- LOG2RAM_SIZE="512M"; CRON_HOURS=6
- fi
-
- msg_ok "$(translate "Detected RAM:") $RAM_SIZE_GB GB — $(translate "Log2RAM size set to:") $LOG2RAM_SIZE"
- pmx_edit_file /etc/log2ram.conf "s/^SIZE=.*/SIZE=$LOG2RAM_SIZE/"
-
- LOG2RAM_BIN="$(command -v log2ram || echo /usr/sbin/log2ram)"
-
- pmx_write_file /etc/cron.d/log2ram </dev/null 2>&1
-EOF
- chmod 0644 /etc/cron.d/log2ram
- chown root:root /etc/cron.d/log2ram
- msg_ok "$(translate "Log2RAM write scheduled every") $CRON_HOURS $(translate "hour(s)")"
-
- pmx_write_file /usr/local/bin/log2ram-check.sh <<'EOF'
-#!/usr/bin/env bash
-# Watch /var/log usage on Log2RAM's tmpfs and act at two thresholds:
-# > 80% → vacuum journald down to ~30% of SIZE, then log2ram write
-# > 92% → aggressive: journal to ~5%, rotate PBS API logs if present,
-# truncate pveproxy/pveam, then log2ram write
-PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
-
-CONF_FILE="/etc/log2ram.conf"
-L2R_BIN="$(command -v log2ram || true)"
-[[ -z "$L2R_BIN" && -x /usr/sbin/log2ram ]] && L2R_BIN="/usr/sbin/log2ram"
-[[ -z "$L2R_BIN" ]] && exit 0
-
-SIZE_MiB="$(grep -E '^SIZE=' "$CONF_FILE" 2>/dev/null | cut -d'=' -f2 | tr -dc '0-9')"
-[[ -z "$SIZE_MiB" ]] && SIZE_MiB=128
-LIMIT_BYTES=$(( SIZE_MiB * 1024 * 1024 ))
-WARN_BYTES=$(( LIMIT_BYTES * 80 / 100 ))
-EMERGENCY_BYTES=$(( LIMIT_BYTES * 92 / 100 ))
-
-USED_BYTES="$(df -B1 --output=used /var/log 2>/dev/null | tail -1 | tr -dc '0-9')"
-[[ -z "$USED_BYTES" ]] && exit 0
-
-LOCK="/run/log2ram-check.lock"
-exec 9>"$LOCK" 2>/dev/null || exit 0
-flock -n 9 || exit 0
-
-if (( USED_BYTES > EMERGENCY_BYTES )); then
- SAFE_JOURNAL_MB=$(( SIZE_MiB * 5 / 100 ))
- [[ "$SAFE_JOURNAL_MB" -lt 16 ]] && SAFE_JOURNAL_MB=16
- journalctl --vacuum-size="${SAFE_JOURNAL_MB}M" >/dev/null 2>&1 || true
- if [[ -x /usr/sbin/logrotate && -f /etc/logrotate.d/proxmox-backup-api ]]; then
- /usr/sbin/logrotate -f /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1 || true
- fi
- # Only truncate if the file already exists. Creating one from
- # this cron path (running as root, default umask) would leave
- # it as root:root 644 — pveproxy runs as www-data and would then
- # fail to reopen it on the next restart, taking :8006 down.
- [ -e /var/log/pveproxy/access.log ] && : > /var/log/pveproxy/access.log 2>/dev/null || true
- [ -e /var/log/pveproxy/error.log ] && : > /var/log/pveproxy/error.log 2>/dev/null || true
- [ -e /var/log/pveam.log ] && : > /var/log/pveam.log 2>/dev/null || true
- "$L2R_BIN" write 2>/dev/null || true
-elif (( USED_BYTES > WARN_BYTES )); then
- SOFT_JOURNAL_MB=$(( SIZE_MiB * 30 / 100 ))
- [[ "$SOFT_JOURNAL_MB" -lt 32 ]] && SOFT_JOURNAL_MB=32
- journalctl --vacuum-size="${SOFT_JOURNAL_MB}M" >/dev/null 2>&1 || true
- "$L2R_BIN" write 2>/dev/null || true
-fi
-EOF
- chmod +x /usr/local/bin/log2ram-check.sh
-
- pmx_write_file /etc/cron.d/log2ram-auto-sync <<'EOF'
-# Log2RAM auto-sync based on /var/log usage - Created by ProxMenux
-SHELL=/bin/bash
-PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
-MAILTO=""
-# nice/ionice keep the check off the priority queue for scheduled tasks.
-3-59/10 * * * * root nice -n 19 ionice -c 3 /usr/local/bin/log2ram-check.sh >/dev/null 2>&1
-EOF
- chmod 0644 /etc/cron.d/log2ram-auto-sync
- chown root:root /etc/cron.d/log2ram-auto-sync
-
- systemctl restart cron >/dev/null 2>&1 || true
- msg_ok "$(translate "Auto-sync enabled when /var/log exceeds 80% of") $LOG2RAM_SIZE"
-
-
- msg_info "$(translate "Adjusting systemd-journald limits to match Log2RAM size...")"
-
-
- if [[ -f /etc/systemd/journald.conf ]]; then
- cp -n /etc/systemd/journald.conf /etc/systemd/journald.conf.bak.$(date +%Y%m%d-%H%M%S)
-
- fi
-
- SIZE_MB=$(echo "$LOG2RAM_SIZE" | tr -dc '0-9')
-
-
- USE_MB=$(( SIZE_MB * 55 / 100 ))
- KEEP_MB=$(( SIZE_MB * 10 / 100 ))
- RUNTIME_MB=$(( SIZE_MB * 25 / 100 ))
-
-
- [ "$USE_MB" -lt 80 ] && USE_MB=80
- [ "$RUNTIME_MB" -lt 32 ] && RUNTIME_MB=32
- [ "$KEEP_MB" -lt 8 ] && KEEP_MB=8
-
-
- # Compose the final file and write it once: keep everything above the
- # existing [Journal] section, then our block. Editing then appending
- # produced two journal entries for one file, each with half the diff;
- # one write shows the whole before/after a sysadmin should read.
- {
- sed '/^\[Journal\]/,$d' /etc/systemd/journald.conf 2>/dev/null
- cat </dev/null 2>&1 || true
- if ! pmx_apply_setting "service-enabled:log2ram" "systemctl is-enabled log2ram" \
- systemctl enable log2ram; then
- msg_error "$(translate "Log2RAM installation verification failed. Check /tmp/log2ram_install.log")"
- return 1
- fi
+ pmx_log2ram_setup "$(pmx_log2ram_default_size)" "$(pmx_log2ram_default_hours)" true || return 1
NECESSARY_REBOOT=1
-
register_tool "log2ram" true "$FUNC_VERSION"
}
diff --git a/scripts/post_install/customizable_post_install.sh b/scripts/post_install/customizable_post_install.sh
index 4b75d553..d3512659 100644
--- a/scripts/post_install/customizable_post_install.sh
+++ b/scripts/post_install/customizable_post_install.sh
@@ -73,6 +73,9 @@ fi
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
fi
+if [[ -f "$LOCAL_SCRIPTS/global/log2ram_setup.sh" ]]; then
+ source "$LOCAL_SCRIPTS/global/log2ram_setup.sh"
+fi
if [[ -f "$LOCAL_SCRIPTS/post_install/zram_profile_helpers.sh" ]]; then
source "$LOCAL_SCRIPTS/post_install/zram_profile_helpers.sh"
fi
@@ -3287,134 +3290,22 @@ update_pve_appliance_manager() {
_update_existing_log2ram_custom() {
local func_version="$1"
- local log2ram_bin=""
- local candidate resolved tmp_file
+ local watch=false
pmx_journal_context "_update_existing_log2ram_custom" "$func_version"
msg_ok "$(translate "Log2RAM already registered — updating to latest configuration")"
-
- for candidate in \
- "$(command -v log2ram 2>/dev/null)" \
- /usr/local/bin/log2ram \
- /usr/sbin/log2ram \
- /usr/bin/log2ram
- do
- [[ -n "$candidate" && -f "$candidate" ]] || continue
- resolved="$(readlink -f "$candidate" 2>/dev/null || printf '%s' "$candidate")"
- [[ -f "$resolved" ]] || continue
- log2ram_bin="$resolved"
- break
- done
-
- if [[ -z "$log2ram_bin" || ! -f /etc/log2ram.conf ]]; then
- msg_error "$(translate "Log2RAM installation verification failed. Check /tmp/log2ram_install.log")"
- return 1
- fi
-
- if grep -q 'rsync -aAXv ' "$log2ram_bin" 2>/dev/null; then
- [[ -e "${log2ram_bin}.proxmenux.bak" ]] || cp -a "$log2ram_bin" "${log2ram_bin}.proxmenux.bak"
- sed 's/rsync -aAXv /rsync -aXv --no-acls /g' "$log2ram_bin" | pmx_write_file "$log2ram_bin"
- fi
-
- if pmx_log2ram_drop_stale_journal "$log2ram_bin"; then
- NECESSARY_REBOOT=1
- msg_ok "$(translate "Log2RAM keeps the journal in order from the next reboot")"
- fi
-
- if dpkg-query -W -f='${Status}' proxmox-backup-server 2>/dev/null \
- | grep -q 'install ok installed'; then
- tmp_file="$(mktemp /etc/logrotate.d/.proxmox-backup-api.XXXXXX)" || return 1
- cat > "$tmp_file" <<'EOF'
-/var/log/proxmox-backup/api/access.log /var/log/proxmox-backup/api/auth.log {
- size 20M
- rotate 3
- compress
- missingok
- notifempty
- copytruncate
-}
-EOF
- chmod 0644 "$tmp_file"
- chown root:root "$tmp_file"
- pmx_write_file /etc/logrotate.d/proxmox-backup-api < "$tmp_file"
- rm -f "$tmp_file"
-
- tmp_file="$(mktemp /etc/cron.hourly/.proxmox-backup-logrotate.XXXXXX)" || return 1
- cat > "$tmp_file" <<'EOF'
-#!/bin/sh
-/usr/sbin/logrotate /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1
-EOF
- chmod 0755 "$tmp_file"
- chown root:root "$tmp_file"
- pmx_write_file /etc/cron.hourly/proxmox-backup-logrotate < "$tmp_file"
- chmod 0755 /etc/cron.hourly/proxmox-backup-logrotate
- chown root:root /etc/cron.hourly/proxmox-backup-logrotate
- rm -f "$tmp_file"
- msg_ok "$(translate "PBS API log rotation configured (hourly, size-based)")"
- fi
-
- if [[ -f /usr/local/bin/log2ram-check.sh ]]; then
- tmp_file="$(mktemp /usr/local/bin/.log2ram-check.XXXXXX)" || return 1
- cat > "$tmp_file" <<'EOF'
-#!/usr/bin/env bash
-# Watch /var/log usage on Log2RAM's tmpfs and act at two thresholds:
-# > 80% → vacuum journald down to ~30% of SIZE, then log2ram write
-# > 92% → aggressive: journal to ~5%, rotate PBS API logs if present,
-# truncate existing pveproxy/pveam logs, then log2ram write
-PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
-CONF_FILE="/etc/log2ram.conf"
-L2R_BIN="$(command -v log2ram || true)"
-[[ -z "$L2R_BIN" && -x /usr/sbin/log2ram ]] && L2R_BIN="/usr/sbin/log2ram"
-[[ -z "$L2R_BIN" ]] && exit 0
-
-SIZE_MiB="$(grep -E '^SIZE=' "$CONF_FILE" 2>/dev/null | cut -d'=' -f2 | tr -dc '0-9')"
-[[ -z "$SIZE_MiB" ]] && SIZE_MiB=128
-LIMIT_BYTES=$(( SIZE_MiB * 1024 * 1024 ))
-WARN_BYTES=$(( LIMIT_BYTES * 80 / 100 ))
-EMERGENCY_BYTES=$(( LIMIT_BYTES * 92 / 100 ))
-
-USED_BYTES="$(df -B1 --output=used /var/log 2>/dev/null | tail -1 | tr -dc '0-9')"
-[[ -z "$USED_BYTES" ]] && exit 0
-
-LOCK="/run/log2ram-check.lock"
-exec 9>"$LOCK" 2>/dev/null || exit 0
-flock -n 9 || exit 0
-
-if (( USED_BYTES > EMERGENCY_BYTES )); then
- SAFE_JOURNAL_MB=$(( SIZE_MiB * 5 / 100 ))
- [[ "$SAFE_JOURNAL_MB" -lt 16 ]] && SAFE_JOURNAL_MB=16
- journalctl --vacuum-size="${SAFE_JOURNAL_MB}M" >/dev/null 2>&1 || true
- if [[ -x /usr/sbin/logrotate && -f /etc/logrotate.d/proxmox-backup-api ]]; then
- /usr/sbin/logrotate -f /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1 || true
- fi
- [ -e /var/log/pveproxy/access.log ] && : > /var/log/pveproxy/access.log 2>/dev/null || true
- [ -e /var/log/pveproxy/error.log ] && : > /var/log/pveproxy/error.log 2>/dev/null || true
- [ -e /var/log/pveam.log ] && : > /var/log/pveam.log 2>/dev/null || true
- "$L2R_BIN" write 2>/dev/null || true
-elif (( USED_BYTES > WARN_BYTES )); then
- SOFT_JOURNAL_MB=$(( SIZE_MiB * 30 / 100 ))
- [[ "$SOFT_JOURNAL_MB" -lt 32 ]] && SOFT_JOURNAL_MB=32
- journalctl --vacuum-size="${SOFT_JOURNAL_MB}M" >/dev/null 2>&1 || true
- "$L2R_BIN" write 2>/dev/null || true
-fi
-EOF
- chmod 0755 "$tmp_file"
- chown root:root "$tmp_file"
- bash -n "$tmp_file" || return 1
- pmx_write_file /usr/local/bin/log2ram-check.sh < "$tmp_file"
- chmod 0755 /usr/local/bin/log2ram-check.sh
- chown root:root /usr/local/bin/log2ram-check.sh
- rm -f "$tmp_file"
- fi
-
+ [[ -f /etc/cron.d/log2ram-auto-sync ]] && watch=true
+ pmx_log2ram_setup "$(pmx_log2ram_current_size)" "$(pmx_log2ram_current_hours)" "$watch" || return 1
+ NECESSARY_REBOOT=1
register_tool "log2ram" true "$func_version"
msg_success "$(translate "Log2RAM installation and configuration completed successfully.")"
}
configure_log2ram() {
- local FUNC_VERSION="1.8"
+ local FUNC_VERSION="2.0"
local existing_log2ram_bin=""
+ local watch=false
pmx_journal_context "configure_log2ram" "$FUNC_VERSION"
# description: Install Log2RAM with a chosen size, sync interval and optional autosync. Updates preserve the size and autosync choice.
@@ -3429,7 +3320,6 @@ configure_log2ram() {
msg_info2 "$(translate "Preparing Log2RAM configuration")"
sleep 1
-
RAM_SIZE_GB=$(awk '/^MemTotal:/{print int($2/1024/1024)}' /proc/meminfo)
[[ -z "$RAM_SIZE_GB" || "$RAM_SIZE_GB" -eq 0 ]] && RAM_SIZE_GB=4
@@ -3467,254 +3357,12 @@ configure_log2ram() {
"12" "$(translate "Every 12 hours")" OFF \
3>&1 1>&2 2>&3) || return 0
-
- if whiptail --title "Log2RAM" --yesno "$(translate "Enable auto-sync if /var/log exceeds 90% of its size?")" 10 60; then
- ENABLE_AUTOSYNC=true
- else
- ENABLE_AUTOSYNC=false
+ if whiptail --title "Log2RAM" --yesno "$(translate "Rotate and write /var/log to disk when it passes 85% of its size?")" 10 60; then
+ watch=true
fi
-
- msg_info "$(translate "Cleaning previous Log2RAM installation...")"
- pmx_disable_service log2ram || true
- pmx_disable_service log2ram-daily.timer || true
-
- local obsolete_path
- for obsolete_path in \
- /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync \
- /etc/cron.hourly/log2ram /etc/cron.daily/log2ram \
- /etc/cron.weekly/log2ram /etc/cron.monthly/log2ram \
- /usr/local/bin/log2ram-check.sh /usr/local/bin/log2ram /usr/sbin/log2ram \
- /etc/systemd/system/log2ram.service \
- /etc/systemd/system/log2ram-daily.timer \
- /etc/systemd/system/log2ram-daily.service \
- /etc/systemd/system/sysinit.target.wants/log2ram.service \
- /etc/log2ram.conf /etc/log2ram.conf.* /etc/logrotate.d/log2ram
- do
- pmx_remove_file "$obsolete_path" 2>/dev/null || true
- done
- rm -rf /etc/systemd/system/log2ram.service.d 2>/dev/null || true
- rm -rf /var/log.hdd /tmp/log2ram 2>/dev/null || true
-
- systemctl daemon-reload >/dev/null 2>&1 || true
- systemctl restart cron >/dev/null 2>&1 || true
- msg_ok "$(translate "Previous installation cleaned")"
-
-
- msg_info "$(translate "Installing Log2RAM from GitHub...")"
- if ! command -v git >/dev/null 2>&1; then
- msg_info "$(translate "Installing required package: git")"
- apt-get update -qq >/dev/null 2>&1
- pmx_install_pkg git
- fi
-
- rm -rf /tmp/log2ram 2>/dev/null || true
- if ! git clone --depth 1 https://github.com/azlux/log2ram.git /tmp/log2ram \
- >/dev/null 2>>/tmp/log2ram_install.log; then
- msg_error "$(translate "Failed to clone log2ram repository. Check /tmp/log2ram_install.log")"
- return 1
- fi
-
- cd /tmp/log2ram || { msg_error "$(translate "Failed to access log2ram directory")"; return 1; }
- if ! bash install.sh >>/tmp/log2ram_install.log 2>&1; then
- msg_error "$(translate "Failed to run log2ram installer. Check /tmp/log2ram_install.log")"
- return 1
- fi
-
- # Drop ACL preservation from the upstream rsync call: some
- # /var/log.hdd filesystems reject POSIX ACLs and log2ram write
- # exits 23 with `set_acl: Operation not supported`. xattrs stay.
- local _l2r_bin
- for _l2r_bin in \
- "$(command -v log2ram 2>/dev/null)" \
- /usr/local/bin/log2ram \
- /usr/sbin/log2ram \
- /usr/bin/log2ram
- do
- [[ -n "$_l2r_bin" && -f "$_l2r_bin" ]] || continue
- if grep -q 'rsync -aAXv ' "$_l2r_bin" 2>/dev/null; then
- cp -a "$_l2r_bin" "${_l2r_bin}.proxmenux.bak"
- pmx_edit_file "$_l2r_bin" 's/rsync -aAXv /rsync -aXv --no-acls /g'
- fi
- pmx_log2ram_drop_stale_journal "$_l2r_bin" || true
- break
- done
-
- # Size-based rotation for the PBS API logs — the upstream package
- # ships no logrotate rule and pvestatd's local-datastore poll fills
- # them fast enough to saturate a tmpfs /var/log.
- if dpkg-query -W -f='${Status}' proxmox-backup-server 2>/dev/null \
- | grep -q 'install ok installed'; then
- mkdir -p /var/log/proxmox-backup/api 2>/dev/null || true
- pmx_write_file /etc/logrotate.d/proxmox-backup-api <<'EOF'
-/var/log/proxmox-backup/api/access.log /var/log/proxmox-backup/api/auth.log {
- size 20M
- rotate 3
- compress
- missingok
- notifempty
- copytruncate
-}
-EOF
- chmod 0644 /etc/logrotate.d/proxmox-backup-api
- chown root:root /etc/logrotate.d/proxmox-backup-api
- pmx_write_file /etc/cron.hourly/proxmox-backup-logrotate <<'EOF'
-#!/bin/sh
-/usr/sbin/logrotate /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1
-EOF
- chmod 0755 /etc/cron.hourly/proxmox-backup-logrotate
- chown root:root /etc/cron.hourly/proxmox-backup-logrotate
- msg_ok "$(translate "PBS API log rotation configured (hourly, size-based)")"
- fi
-
- systemctl daemon-reload >/dev/null 2>&1 || true
-
- if [[ -f /etc/log2ram.conf ]] && command -v log2ram >/dev/null 2>&1; then
- msg_ok "$(translate "Log2RAM installed successfully")"
- else
- msg_error "$(translate "Log2RAM installation verification failed. Check /tmp/log2ram_install.log")"
- return 1
- fi
-
-
- pmx_edit_file /etc/log2ram.conf "s/^SIZE=.*/SIZE=$LOG2RAM_SIZE/"
- LOG2RAM_BIN="$(command -v log2ram || echo /usr/sbin/log2ram)"
-
- pmx_write_file /etc/cron.d/log2ram </dev/null 2>&1
-EOF
- chmod 0644 /etc/cron.d/log2ram
- chown root:root /etc/cron.d/log2ram
- msg_ok "$(translate "Log2RAM write scheduled every") $CRON_HOURS $(translate "hour(s)")"
-
-
- if [[ "$ENABLE_AUTOSYNC" == true ]]; then
- pmx_write_file /usr/local/bin/log2ram-check.sh <<'EOF'
-#!/usr/bin/env bash
-# Watch /var/log usage on Log2RAM's tmpfs and act at two thresholds:
-# > 80% → vacuum journald down to ~30% of SIZE, then log2ram write
-# > 92% → aggressive: journal to ~5%, rotate PBS API logs if present,
-# truncate existing pveproxy/pveam logs, then log2ram write
-PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
-CONF_FILE="/etc/log2ram.conf"
-L2R_BIN="$(command -v log2ram || true)"
-[[ -z "$L2R_BIN" && -x /usr/sbin/log2ram ]] && L2R_BIN="/usr/sbin/log2ram"
-[[ -z "$L2R_BIN" ]] && exit 0
-
-SIZE_MiB="$(grep -E '^SIZE=' "$CONF_FILE" 2>/dev/null | cut -d'=' -f2 | tr -dc '0-9')"
-[[ -z "$SIZE_MiB" ]] && SIZE_MiB=128
-LIMIT_BYTES=$(( SIZE_MiB * 1024 * 1024 ))
-WARN_BYTES=$(( LIMIT_BYTES * 80 / 100 ))
-EMERGENCY_BYTES=$(( LIMIT_BYTES * 92 / 100 ))
-
-USED_BYTES="$(df -B1 --output=used /var/log 2>/dev/null | tail -1 | tr -dc '0-9')"
-[[ -z "$USED_BYTES" ]] && exit 0
-
-LOCK="/run/log2ram-check.lock"
-exec 9>"$LOCK" 2>/dev/null || exit 0
-flock -n 9 || exit 0
-
-if (( USED_BYTES > EMERGENCY_BYTES )); then
- SAFE_JOURNAL_MB=$(( SIZE_MiB * 5 / 100 ))
- [[ "$SAFE_JOURNAL_MB" -lt 16 ]] && SAFE_JOURNAL_MB=16
- journalctl --vacuum-size="${SAFE_JOURNAL_MB}M" >/dev/null 2>&1 || true
- if [[ -x /usr/sbin/logrotate && -f /etc/logrotate.d/proxmox-backup-api ]]; then
- /usr/sbin/logrotate -f /etc/logrotate.d/proxmox-backup-api >/dev/null 2>&1 || true
- fi
- [ -e /var/log/pveproxy/access.log ] && : > /var/log/pveproxy/access.log 2>/dev/null || true
- [ -e /var/log/pveproxy/error.log ] && : > /var/log/pveproxy/error.log 2>/dev/null || true
- [ -e /var/log/pveam.log ] && : > /var/log/pveam.log 2>/dev/null || true
- "$L2R_BIN" write 2>/dev/null || true
-elif (( USED_BYTES > WARN_BYTES )); then
- SOFT_JOURNAL_MB=$(( SIZE_MiB * 30 / 100 ))
- [[ "$SOFT_JOURNAL_MB" -lt 32 ]] && SOFT_JOURNAL_MB=32
- journalctl --vacuum-size="${SOFT_JOURNAL_MB}M" >/dev/null 2>&1 || true
- "$L2R_BIN" write 2>/dev/null || true
-fi
-EOF
- chmod +x /usr/local/bin/log2ram-check.sh
-
- pmx_write_file /etc/cron.d/log2ram-auto-sync <<'EOF'
-# Log2RAM auto-sync based on /var/log usage - Created by ProxMenux
-SHELL=/bin/bash
-PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
-MAILTO=""
-# nice/ionice keep the check off the priority queue for scheduled tasks.
-3-59/10 * * * * root nice -n 19 ionice -c 3 /usr/local/bin/log2ram-check.sh >/dev/null 2>&1
-EOF
- chmod 0644 /etc/cron.d/log2ram-auto-sync
- chown root:root /etc/cron.d/log2ram-auto-sync
- msg_ok "$(translate "Auto-sync enabled when /var/log exceeds 80% of") $LOG2RAM_SIZE"
- else
- pmx_remove_file /usr/local/bin/log2ram-check.sh 2>/dev/null || true
- pmx_remove_file /etc/cron.d/log2ram-auto-sync 2>/dev/null || true
- msg_info2 "$(translate "Auto-sync was not enabled")"
- fi
-
- # --- Ajuste de systemd-journald proporcional al tamaño de Log2RAM ---
- msg_info "$(translate "Adjusting systemd-journald limits to match Log2RAM size...")"
-
- if [[ -f /etc/systemd/journald.conf ]]; then
- cp -n /etc/systemd/journald.conf "/etc/systemd/journald.conf.bak.$(date +%Y%m%d-%H%M%S)"
- BAK_OK=$?
- fi
-
- SIZE_MB=$(echo "$LOG2RAM_SIZE" | tr -dc '0-9')
- # Repartos: 55% persistente / 10% libre / 25% runtime (pisos mínimos)
- USE_MB=$(( SIZE_MB * 55 / 100 ))
- KEEP_MB=$(( SIZE_MB * 10 / 100 ))
- RUNTIME_MB=$(( SIZE_MB * 25 / 100 ))
- [ "$USE_MB" -lt 80 ] && USE_MB=80
- [ "$RUNTIME_MB" -lt 32 ] && RUNTIME_MB=32
- [ "$KEEP_MB" -lt 8 ] && KEEP_MB=8
-
- # Reescribir bloque [Journal] de forma segura
- pmx_edit_file /etc/systemd/journald.conf '/^\[Journal\]/,$d' 2>/dev/null || true
- pmx_append_file /etc/systemd/journald.conf </dev/null 2>&1 || true
- if ! pmx_apply_setting "service-enabled:log2ram" "systemctl is-enabled log2ram" \
- systemctl enable log2ram; then
- msg_error "$(translate "Log2RAM installation verification failed. Check /tmp/log2ram_install.log")"
- return 1
- fi
+ pmx_log2ram_setup "$USER_SIZE" "$CRON_HOURS" "$watch" || return 1
NECESSARY_REBOOT=1
-
msg_success "$(translate "Log2RAM installation and configuration completed successfully.")"
register_tool "log2ram" true "$FUNC_VERSION"
}
diff --git a/scripts/post_install/uninstall-tools.sh b/scripts/post_install/uninstall-tools.sh
index 570a8bf3..498fc02c 100644
--- a/scripts/post_install/uninstall-tools.sh
+++ b/scripts/post_install/uninstall-tools.sh
@@ -37,6 +37,9 @@ fi
if [[ -f "$LOCAL_SCRIPTS/global/pmx_journal.sh" ]]; then
source "$LOCAL_SCRIPTS/global/pmx_journal.sh"
fi
+if [[ -f "$LOCAL_SCRIPTS/global/log2ram_setup.sh" ]]; then
+ source "$LOCAL_SCRIPTS/global/log2ram_setup.sh"
+fi
if [[ -f "$LOCAL_SCRIPTS/post_install/zram_profile_helpers.sh" ]]; then
source "$LOCAL_SCRIPTS/post_install/zram_profile_helpers.sh"
fi
@@ -735,74 +738,9 @@ uninstall_bashrc_custom() {
################################################################
uninstall_log2ram() {
- msg_info "$(translate "Uninstalling log2ram (all versions)...")"
-
- systemctl stop log2ram log2ram-daily.timer log2ram-daily.service >/dev/null 2>&1 || true
- systemctl disable log2ram log2ram-daily.timer log2ram-daily.service >/dev/null 2>&1 || true
-
- rm -f /etc/cron.d/log2ram \
- /etc/cron.d/log2ram-auto-sync \
- /etc/cron.d/log2ram-sync \
- /etc/cron.hourly/log2ram \
- /etc/cron.daily/log2ram \
- /etc/cron.weekly/log2ram \
- /etc/cron.monthly/log2ram 2>/dev/null || true
-
- rm -f /usr/local/bin/log2ram-check.sh \
- /usr/local/bin/log2ram \
- /usr/local/bin/log2ram-sync \
- /usr/sbin/log2ram \
- /usr/bin/log2ram 2>/dev/null || true
-
- rm -f /etc/systemd/system/log2ram.service \
- /etc/systemd/system/log2ram-daily.timer \
- /etc/systemd/system/log2ram-daily.service \
- /etc/systemd/system/sysinit.target.wants/log2ram.service \
- /etc/systemd/system/timers.target.wants/log2ram-daily.timer \
- /lib/systemd/system/log2ram.service \
- /lib/systemd/system/log2ram-daily.timer \
- /lib/systemd/system/log2ram-daily.service 2>/dev/null || true
- rm -rf /etc/systemd/system/log2ram.service.d 2>/dev/null || true
-
- rm -f /etc/log2ram.conf \
- /etc/log2ram.conf.dpkg-old \
- /etc/log2ram.conf.bak \
- /etc/log2ram.conf.save 2>/dev/null || true
-
- rm -rf /etc/logrotate.d/log2ram 2>/dev/null || true
-
- if mountpoint -q /var/log 2>/dev/null; then
- if [[ -d /var/log.hdd ]]; then
- msg_info "$(translate "Preserving logs to /var/log.hdd before unmounting...")"
- rsync -a /var/log/ /var/log.hdd/ >/dev/null 2>&1 || true
- fi
- umount /var/log >/dev/null 2>&1 || true
- fi
-
- [[ -d /var/log.hdd ]] && rm -rf /var/log.hdd
- [[ -d /tmp/log2ram ]] && rm -rf /tmp/log2ram
- [[ -d /var/hdd.log ]] && rm -rf /var/hdd.log
- [[ -f /tmp/log2ram_install.log ]] && rm -f /tmp/log2ram_install.log
-
- systemctl daemon-reload >/dev/null 2>&1 || true
- systemctl reset-failed >/dev/null 2>&1 || true
- systemctl restart cron >/dev/null 2>&1 || true
-
- if dpkg -l 2>/dev/null | grep -q '^ii log2ram'; then
- msg_info "$(translate "Purging log2ram apt package...")"
- apt-get purge -y log2ram >/dev/null 2>&1 || true
- apt-get autoremove -y >/dev/null 2>&1 || true
- fi
-
- if [[ -f /etc/log2ram.conf ]] || \
- command -v log2ram >/dev/null 2>&1 || \
- systemctl list-units --all 2>/dev/null | grep -q log2ram || \
- [[ -f /etc/cron.d/log2ram-auto-sync ]]; then
- msg_warn "$(translate "Some log2ram files may still exist. Manual cleanup may be required.")"
- else
- msg_ok "$(translate "log2ram completely removed from system")"
- fi
-
+ msg_info "$(translate "Uninstalling Log2RAM...")"
+ pmx_log2ram_remove
+ msg_ok "$(translate "Log2RAM is removed at the next reboot, after writing /var/log to disk")"
register_tool "log2ram" false
NECESSARY_REBOOT=1
}
diff --git a/tests/test_lvm_disk_read_policy.py b/tests/test_lvm_disk_read_policy.py
index 30495950..26fd72df 100644
--- a/tests/test_lvm_disk_read_policy.py
+++ b/tests/test_lvm_disk_read_policy.py
@@ -4,6 +4,7 @@ import json
from pathlib import Path
import subprocess
import sys
+import tempfile
import types
import unittest
from unittest.mock import Mock, patch
@@ -147,5 +148,65 @@ class LvmReaderTests(unittest.TestCase):
self.assertIn('cp "$SCRIPT_DIR/lvm_device_scope.py" "$APP_DIR/usr/bin/"', (SCRIPTS / "build_appimage.sh").read_text())
+class QuietDiskIdentityTests(unittest.TestCase):
+ """A quiet rotational disk the Monitor knows nothing of yet, as after a
+ restart, is still shown by its model, serial and type."""
+
+ def setUp(self):
+ folder = tempfile.TemporaryDirectory()
+ self.addCleanup(folder.cleanup)
+ root = Path(folder.name)
+ for name, value in (("sys/block/sdb/device/model", "WDC WD40EFRX-68N\n"), ("sys/block/sdb/dev", "8:16\n"),
+ ("sys/block/sdb/queue/rotational", "1\n"),
+ ("run/udev/data/b8:16", "E:ID_MODEL=WDC_WD40EFRX-68N32N0\nE:ID_SERIAL_SHORT=WD-WCC7K4FV37J0\n")):
+ (root / name).parent.mkdir(parents=True, exist_ok=True)
+ (root / name).write_text(value)
+ self.policy = "idle"
+ self.disk_module = types.ModuleType("disk_temperature_history")
+ self.disk_module.disk_read_policy = lambda disk: self.policy
+ self.disk_module.settle_own_reads = Mock()
+ modules = patch.dict(sys.modules, disk_temperature_history=self.disk_module)
+ modules.start()
+ self.addCleanup(modules.stop)
+ self.standby = Mock(return_value=False)
+ self.read = Mock(return_value={"model": "WDC WD40EFRX-68N32N0", "serial": "WD-WCC7K4FV37J0",
+ "rotation_rate": 5400, "health": "healthy", "temperature": 31})
+ self.cache = {}
+ namespace = {"Path": lambda path: root / path.lstrip("/"), "time": __import__("time"),
+ "_smart_result_cache": self.cache, "_SMART_RESULT_TTL": 30, "_hdd_in_standby": self.standby,
+ "_get_smart_data_uncached": self.read, "_smart_data_useful": lambda data: data.get("model") != "Unknown",
+ "_smart_fail_backoff": {}, "_smart_fail_counts": {}, "_smart_probe_cache": {},
+ "_SMART_FAIL_THRESHOLD": 3, "_SMART_FAIL_BACKOFF_SEC": 3600}
+ tree = ast.parse((SCRIPTS / "flask_server.py").read_text())
+ nodes = [n for n in tree.body if isinstance(n, ast.FunctionDef)
+ and n.name in ("_smart_default_payload", "_smart_identity_payload", "get_smart_data")]
+ exec(compile(ast.Module(body=nodes, type_ignores=[]), "flask_server.py", "exec"), namespace)
+ self.get = namespace["get_smart_data"]
+
+ def test_a_spinning_disk_is_read_once(self):
+ data = self.get("sdb")
+ self.assertEqual((data["model"], data["rotation_rate"], data["health"], data["idle"]),
+ ("WDC WD40EFRX-68N32N0", 5400, "healthy", True))
+ self.disk_module.settle_own_reads.assert_called_once_with(["sdb"])
+ self.cache["sdb"] = (0, self.cache["sdb"][1])
+ self.get("sdb")
+ self.read.assert_called_once()
+ self.assertEqual(self.standby.call_count, 1)
+
+ def test_a_parked_disk_is_not_woken_and_keeps_its_identity(self):
+ self.standby.return_value = True
+ data = self.get("sdb")
+ self.read.assert_not_called()
+ self.assertEqual((data["model"], data["serial"], data["rotation_rate"], data["idle"]),
+ ("WDC WD40EFRX-68N32N0", "WD-WCC7K4FV37J0", -1, True))
+
+ def test_an_excluded_disk_receives_no_command(self):
+ self.policy = "excluded"
+ data = self.get("sdb")
+ self.standby.assert_not_called()
+ self.read.assert_not_called()
+ self.assertEqual((data["model"], data["rotation_rate"], data["excluded"]), ("WDC WD40EFRX-68N32N0", -1, True))
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/tests/test_overview_chart_axis_gutters.cjs b/tests/test_overview_chart_axis_gutters.cjs
new file mode 100644
index 00000000..7983d377
--- /dev/null
+++ b/tests/test_overview_chart_axis_gutters.cjs
@@ -0,0 +1,118 @@
+// Offline source contract, not a pixel/layout test. Real-browser acceptance must
+// separately measure the shipped SVG labels across widths, locales and themes.
+const assert = require('node:assert/strict')
+const fs = require('node:fs')
+const path = require('node:path')
+const { test } = require('node:test')
+const app = path.resolve(__dirname, '../AppImage')
+const ts = require(require.resolve('typescript', { paths: [app] }))
+
+function descendants(node, predicate) {
+ const found = []
+ function visit(child) {
+ if (predicate(child)) found.push(child)
+ ts.forEachChild(child, visit)
+ }
+ visit(node)
+ return found
+}
+
+function attribute(opening, name) {
+ assert.ok(opening.attributes.properties.every(ts.isJsxAttribute), 'explicit JSX props required')
+ const matches = opening.attributes.properties.filter(prop => prop.name.text === name)
+ assert.ok(matches.length <= 1, `duplicate ${name} prop`)
+ const value = matches[0]?.initializer
+ return value && ts.isJsxExpression(value) ? value.expression : value
+}
+
+function property(object, name) {
+ assert.ok(object && ts.isObjectLiteralExpression(object), 'explicit object required')
+ assert.ok(object.properties.every(ts.isPropertyAssignment), 'explicit object properties required')
+ const matches = object.properties.filter(prop => prop.name.text === name)
+ assert.ok(matches.length <= 1, `duplicate ${name} property`)
+ return matches[0]?.initializer
+}
+
+// Compare expression meaning at this small seam, ignoring whitespace/comments.
+// Unsupported syntax fails closed instead of silently accepting a missing label.
+function expression(node) {
+ assert.ok(node, 'label expression must remain present')
+ if (ts.isParenthesizedExpression(node)) return expression(node.expression)
+ if (ts.isStringLiteral(node)) return node.text
+ if (ts.isNumericLiteral(node)) return Number(node.text)
+ if (ts.isIdentifier(node)) return { identifier: node.text }
+ if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken) {
+ return -expression(node.operand)
+ }
+ if (ts.isCallExpression(node)) return { call: expression(node.expression), args: node.arguments.map(expression) }
+ if (ts.isBinaryExpression(node)) {
+ return { operator: ts.tokenToString(node.operatorToken.kind), left: expression(node.left), right: expression(node.right) }
+ }
+ if (ts.isConditionalExpression(node)) {
+ return { condition: expression(node.condition), yes: expression(node.whenTrue), no: expression(node.whenFalse) }
+ }
+ if (ts.isObjectLiteralExpression(node)) {
+ assert.ok(node.properties.every(ts.isPropertyAssignment), 'explicit label properties required')
+ const names = node.properties.map(prop => prop.name.text)
+ assert.equal(new Set(names).size, names.length, 'unique label properties required')
+ return Object.fromEntries(node.properties.map(prop => [prop.name.text, expression(prop.initializer)]))
+ }
+ assert.fail(`unsupported contract expression: ${ts.SyntaxKind[node.kind]}`)
+}
+
+function charts(file, component, expectedCount) {
+ const source = ts.createSourceFile(file, fs.readFileSync(path.join(app, 'components', file), 'utf8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX)
+ assert.equal(source.parseDiagnostics.length, 0, `${file} must parse`)
+ const imports = source.statements.filter(node => ts.isImportDeclaration(node) && node.moduleSpecifier.text === 'recharts')
+ const bindings = imports.flatMap(node => node.importClause?.namedBindings?.elements || [])
+ const localName = name => {
+ const binding = bindings.find(node => (node.propertyName || node.name).text === name)
+ assert.ok(binding, `${name} must come from recharts`)
+ return binding.name.text
+ }
+ const [areaChartName, areaName, yAxisName] = ['AreaChart', 'Area', 'YAxis'].map(localName)
+ const functions = source.statements.filter(node => ts.isFunctionDeclaration(node) && node.name?.text === component)
+ assert.equal(functions.length, 1, `${component} must remain present`)
+ assert.ok(functions[0].modifiers?.some(node => node.kind === ts.SyntaxKind.ExportKeyword), `${component} must remain exported`)
+ const result = descendants(functions[0].body, node => ts.isJsxElement(node) && node.openingElement.tagName.getText(source) === areaChartName)
+ assert.equal(result.length, expectedCount, `${component}: check every shipped AreaChart`)
+ return result.map(chart => ({
+ margin: attribute(chart.openingElement, 'margin'),
+ keys: chart.children.filter(ts.isJsxSelfClosingElement).filter(node => node.tagName.getText(source) === areaName).map(node => expression(attribute(node, 'dataKey'))),
+ axes: chart.children.filter(ts.isJsxSelfClosingElement).filter(node => node.tagName.getText(source) === yAxisName),
+ }))
+}
+
+const nodeCharts = charts('node-metrics-charts.tsx', 'NodeMetricsCharts', 2)
+const networkCharts = charts('network-traffic-chart.tsx', 'NetworkTrafficChart', 1)
+function chartFor(charts, key, axisCount) {
+ const matches = charts.filter(chart => chart.keys.includes(key))
+ assert.equal(matches.length, 1, `one actual ${key} chart required`)
+ assert.equal(matches[0].axes.length, axisCount, `${key}: labeled axes must remain`)
+ return matches[0]
+}
+const cpu = chartFor(nodeCharts, 'cpu', 2)
+const memory = chartFor(nodeCharts, 'memoryTotal', 1)
+const network = chartFor(networkCharts, 'netIn', 1)
+const identifier = name => ({ identifier: name })
+const desktopLabel = (value, side) => ({ value, angle: side === 'left' ? -90 : 90, position: side === 'left' ? 'insideLeft' : 'insideRight', fill: 'currentColor' })
+const mobileRule = label => ({ condition: identifier('isMobile'), yes: identifier('undefined'), no: label })
+const contracts = [
+ ['CPU %', cpu, 'left', mobileRule(desktopLabel('CPU %', 'left'))],
+ ['CPU Load', cpu, 'right', mobileRule(desktopLabel({ call: identifier('t'), args: ['overview.loadAxis'] }, 'right'))],
+ ['Memory GB', memory, 'left', mobileRule(desktopLabel('GB', 'left'))],
+ ['Network GB/Gb', network, 'left', desktopLabel({ condition: { operator: '===', left: identifier('networkUnit'), right: 'Bits' }, yes: 'Gb', no: 'GB' }, 'left')],
+]
+for (const [name, chart, side, label] of contracts) {
+ test(`${name}: label retained with >=8px internal ${side} gutter (source contract)`, () => {
+ const axes = chart.axes.filter(axis => {
+ const orientation = attribute(axis, 'orientation')
+ return (orientation ? expression(orientation) : 'left') === side
+ })
+ assert.equal(axes.length, 1, `${name}: one ${side} YAxis required`)
+ assert.deepEqual(expression(attribute(axes[0], 'label')), label, `${name}: preserve label, unit and mobile rule`)
+ const gutterNode = property(chart.margin, side)
+ const gutter = gutterNode ? expression(gutterNode) : 0
+ assert.ok(typeof gutter === 'number' && gutter >= 8, `${name}: ${side} AreaChart margin must be >=8px; got ${JSON.stringify(gutter)}`)
+ })
+}
diff --git a/tests/test_update_confirmation.py b/tests/test_update_confirmation.py
new file mode 100644
index 00000000..36281fd4
--- /dev/null
+++ b/tests/test_update_confirmation.py
@@ -0,0 +1,126 @@
+"""Exercise only the update confirmation widget in a disposable terminal.
+
+No update script is sourced or executed: the isolated UI command receives
+synthetic package names. Requires dialog; skips on systems without it.
+"""
+
+import errno
+import fcntl
+import os
+from pathlib import Path
+import pty
+import re
+import select
+import shutil
+import signal
+import struct
+import termios
+import time
+import unittest
+
+
+ROOT = Path(__file__).resolve().parents[1]
+UPDATERS = (
+ "update-pve8.sh",
+ "update-pve9_2.sh",
+ "update-pve-safe.sh",
+)
+
+
+def confirmation_command(name):
+ source = (ROOT / "scripts/global" / name).read_text()
+ match = re.search(
+ r'^\s*if (?:! )?((?:dialog|whiptail) --(?:keep-tite --)?title '
+ r'"\$\(translate "Proxmox Update"\)"[^;]+); then',
+ source,
+ re.MULTILINE,
+ )
+ if not match:
+ raise AssertionError("Update confirmation not found: " + name)
+ return match.group(1)
+
+
+def run_widget(command, text, keys):
+ child, terminal = pty.fork()
+ if child == 0:
+ fcntl.ioctl(1, termios.TIOCSWINSZ, struct.pack("HHHH", 30, 100, 0, 0))
+ environment = dict(os.environ, TERM="xterm-256color", LC_ALL="C",
+ DIALOGRC="/dev/null", UPDATE_CONFIRMATION_TEXT=text)
+ # Exit-code overrides must not leak into this isolated test.
+ for key in list(environment):
+ if key.startswith("DIALOG_"):
+ environment.pop(key)
+ script = ('translate() { printf "%s" "$1"; }\n'
+ 'menu_text=$UPDATE_CONFIRMATION_TEXT\n' + command)
+ os.execve("/bin/bash", ["bash", "-c", script], environment)
+
+ output = bytearray()
+ sent = False
+ status = None
+ deadline = time.monotonic() + 8
+ try:
+ while time.monotonic() < deadline:
+ if select.select([terminal], [], [], 0.1)[0]:
+ try:
+ chunk = os.read(terminal, 65536)
+ except OSError as error:
+ if error.errno != errno.EIO:
+ raise
+ chunk = b""
+ output.extend(chunk)
+ # The widget is ready once both buttons have been drawn.
+ plain = re.sub(rb"\x1b(?:\[[0-?]*[ -/]*[@-~]|[()][A-Za-z0-9]|.)", b"", output)
+ if not sent and b"Yes" in plain and b"No" in plain:
+ os.write(terminal, keys)
+ sent = True
+ finished, result = os.waitpid(child, os.WNOHANG)
+ if finished:
+ status = os.waitstatus_to_exitcode(result)
+ break
+ if status is None:
+ raise AssertionError("Confirmation did not respond to keyboard input: " + repr(bytes(output[-2000:])))
+ return status, bytes(output)
+ finally:
+ if status is None:
+ try:
+ os.kill(child, signal.SIGKILL)
+ except ProcessLookupError:
+ pass
+ os.waitpid(child, 0)
+ os.close(terminal)
+
+
+@unittest.skipUnless(shutil.which("dialog"), "dialog is not installed")
+class UpdateConfirmationTest(unittest.TestCase):
+ def test_enter_confirms_yes_without_tab_for_short_and_long_lists(self):
+ for updater in UPDATERS:
+ for count in (1, 80):
+ with self.subTest(updater=updater, packages=count):
+ text = "System Update Information\\n\\n" + "\\n".join(
+ " package-{:02d} 1.0 -> 2.0".format(i)
+ for i in range(count)
+ ) + "\\n\\nDo you want to proceed with the system update?"
+ status, output = run_widget(confirmation_command(updater), text, b"\r")
+ self.assertEqual(status, 0, output)
+
+ def test_tab_can_select_no_and_escape_can_cancel(self):
+ for updater in UPDATERS:
+ for keys, expected in ((b"\t\r", 1), (b"\x1b", 255)):
+ with self.subTest(updater=updater, keys=keys):
+ status, output = run_widget(confirmation_command(updater), "Confirm update?", keys)
+ self.assertEqual(status, expected, output)
+
+ def test_full_list_remains_scrollable_with_yes_focused(self):
+ text = "\\n".join("package-{:03d}".format(i) for i in range(80))
+ for updater in UPDATERS:
+ with self.subTest(updater=updater):
+ # G scrolls to the end of the yes/no text without moving focus.
+ status, output = run_widget(confirmation_command(updater), text, b"G\r")
+ self.assertEqual(status, 0, output)
+ # Curses may repaint only changed digits rather than emit a
+ # complete final line. Its 100% indicator confirms the end.
+ self.assertIn(b"100%", output)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/web/messages/de/docs/post-install/automated.json b/web/messages/de/docs/post-install/automated.json
index fc4ab3fb..c25d2438 100644
--- a/web/messages/de/docs/post-install/automated.json
+++ b/web/messages/de/docs/post-install/automated.json
@@ -79,7 +79,7 @@
},
{
"tool": "Log2RAM (SSD-bewusst, auto)",
- "what": "Reduziert SSD-/NVMe-Verschleiß durch Verschieben von /var/log in eine tmpfs-RAM-Disk mit regelmäßiger Synchronisierung auf Datenträger. Installation aus dem Upstream-Projekt bei SSD oder NVMe als Systemplatte. Die Größe richtet sich nach dem Host-RAM (128M / 256M / 512M). Ein Schutz leert alte Journale ab 80% Belegung und kürzt stark wachsende Logs ab 92% vor dem Schreiben. Regelmäßige Synchronisierung wird geplant und journald an den verfügbaren Platz angepasst. Details unter Log2RAM.",
+ "what": "Reduziert SSD-/NVMe-Verschleiß durch Verschieben von /var/log in eine tmpfs-RAM-Disk, die nach Zeitplan auf den Datenträger geschrieben wird. Installation aus dem Upstream-Projekt bei SSD oder NVMe als Systemplatte. Die Größe richtet sich nach dem Host-RAM (128M / 256M / 512M); eine Überwachung rotiert und schreibt die Logs, wenn die RAM-Disk 85 % überschreitet. Das Journal und die Task-Logs von Proxmox VE und Proxmox Backup Server bleiben auf dem Datenträger. Alle Details unter Log2RAM.",
"category": "Speicher",
"categorySlug": "storage"
},
@@ -109,7 +109,7 @@
}
],
"hardwareTitle": "Hardwareabhängige Standardwerte",
- "hardwareBody": "Das Skript liest die Host-Hardware vor dem Anwenden von Einstellungen. Log2RAM ist standardmäßig nur auf SSD / NVMe Root-Festplatten installiert (es wird aufgefordert, wenn die Erkennung etwas anderes sagt). Die Ramdisk-Größe von Log2RAM wird aus der Gesamt-RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M) ausgewählt. Die Journald-Limits werden an die Ramdisk-Größe angepasst, wenn Log2RAM aktiv ist, um ein Ausfüllen zu vermeiden.",
+ "hardwareBody": "Das Skript liest die Host-Hardware vor dem Anwenden von Einstellungen. Log2RAM ist standardmäßig nur auf SSD / NVMe Root-Festplatten installiert (es wird aufgefordert, wenn die Erkennung etwas anderes sagt). Die Ramdisk-Größe von Log2RAM wird aus der Gesamt-RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M) ausgewählt. Bei aktivem Log2RAM bleibt das Journal mit eigener Grenze auf dem Datenträger.",
"upgrade": {
"heading": "Paketlisten und System aktualisieren",
"intro": "Der erste Schritt der automatisierten Nachinstallation ist das Proxmox-Upgrade. Für einen laufenden Host innerhalb derselben Hauptversion empfiehlt Proxmox selbst:",
diff --git a/web/messages/de/docs/post-install/optional.json b/web/messages/de/docs/post-install/optional.json
index ea729e99..46939c73 100644
--- a/web/messages/de/docs/post-install/optional.json
+++ b/web/messages/de/docs/post-install/optional.json
@@ -133,25 +133,25 @@
},
"log2ram": {
"title": "Log2RAM installieren (SSD/NVMe Verschleißreduzierung)",
- "intro": "Log2RAM bindet /var/log als tmpfs-RAM-Disk ein und synchronisiert regelmäßig auf den Datenträger. Das Puffern häufiger journald-Schreibvorgänge im RAM kann SSD-Verschleiß und Datenträger-I/O verringern. Synchronisierung und sauberes Herunterfahren sichern die Logs; bei abruptem Stromausfall können Einträge seit der letzten Synchronisierung verloren gehen.",
+ "intro": "Log2RAM bindet /var/log als tmpfs-RAM-Disk ein und schreibt sie nach Zeitplan auf den Datenträger, sodass ständig wechselnde Logs wie das Zugriffslog von pveproxy in den RAM statt auf die System-SSD geschrieben werden. Das Journal und die Task-Logs von Proxmox VE und Proxmox Backup Server bleiben auf dem Datenträger. Ein sauberes Herunterfahren schreibt die RAM-Disk auf den Datenträger; bei einem abrupten Stromausfall gehen nur die seit der letzten Synchronisierung geschriebenen Textlogs verloren.",
"upstreamLabel": "Vorgelagertes Projekt:",
"upstreamUrl": "https://github.com/azlux/log2ram",
"upstreamLinkLabel": "azlux/log2ram auf GitHub",
"doesLabel": "Was ProxMenux tut:",
"doesItems": [
"Erkennt anhand von /sys/block/<dev>/queue/rotational, ob die Systemplatte SSD/NVMe ist. Bei einer rotierenden Festplatte fragt die automatisierte Variante vor der Installation nach.",
- "Klont das Originalprojekt (azlux/log2ram) nach /tmp/log2ram und passt install.sh an: rsync -aAXv wird durch rsync -aXv --no-acls ersetzt. Dadurch wird der Fehler set_acl: Operation not supported mit Exitcode 23 vermieden, der beim Verschieben von /var/log auf Dateisystemen ohne ACL-Unterstützung auftreten kann. Die systemd-Unit log2ram wird nach Installation aktiviert.",
+ "Installiert Log2RAM 1.7.2 aus dem Upstream-Projekt, prüft, dass der Download genau diese Version ist, und lässt den Code unverändert. Die Fehler-E-Mail wird mit NOTIFICATION=false abgeschaltet, da Proxmox VE keinen Mail-Befehl mitbringt.",
"Größe der Ramdisk auf Basis des Hosts RAM: ≤ 8 GB → 128M, ≤ 16 GB → 256M, > 16 GB → 512M. Schreibt den Wert in SIZE= in /etc/log2ram.conf.",
- "Plant eine regelmäßige Datenträgersynchronisierung über /etc/cron.d/log2ram: alle 1 / 3 / 6 Stunden entsprechend derselben RAM-Stufe.",
- "Installiert den Schutz /usr/local/bin/log2ram-check.sh, der über /etc/cron.d/log2ram-auto-sync alle 10 Minuten läuft. Bei 80% Belegung von /var/log bereinigt er journald. Bei 92% führt er zunächst logrotate -f /etc/logrotate.d/proxmox-backup-api aus, falls die Datei existiert: Die PBS-API-Logs sind oft der größte Platzverbraucher auf einem PBS-Host. Danach kürzt er pveproxy access/error und pveam.log vor der Synchronisierung. log2ram write allein kopiert tmpfs auf Disk, verkleinert es aber nicht. Der Schutz hilft gegen PVE-Ausfälle mit No space left on device durch unkontrolliertes Logwachstum.",
- "Erkennt den Dienst proxmox-backup-server auf dem Host. Falls vorhanden, erstellt es /etc/logrotate.d/proxmox-backup-api (20 MB × 3 Rotationen) und /etc/cron.hourly/proxmox-backup-logrotate, damit die häufig stark wachsenden PBS-API-Logs auch zwischen Log2RAM-Synchronisierungen begrenzt werden. Ohne PBS erfolgt keine Änderung.",
- "Stellt systemd-journald-Grenzwerte (SystemMaxUse, RuntimeMaxUse) so ein, dass sie in die Ramdisk passen, so dass ein einzelner Burst sie nicht füllen kann.",
- "Wird in installed_tools.json erfasst und kann über Optimierungen deinstallieren rückgängig gemacht werden."
+ "Hält das Journal (/var/log/journal), die Task-Logs von Proxmox VE (/var/log/pve) und, falls installiert, die Logs von Proxmox Backup Server (/var/log/proxmox-backup) auf dem Datenträger, in /var/hdd.journal, /var/hdd.pve und /var/hdd.proxmox-backup. Jeder übliche Pfad wird zu einem Link auf seinen Ordner auf dem Datenträger. Die Verschiebung erfolgt beim Start, bevor Log2RAM startet.",
+ "Schreibt die RAM-Disk mit dem Log2RAM-Timer alle 1 / 3 / 6 Stunden entsprechend derselben RAM-Stufe auf den Datenträger und leert nach jedem Schreiben den Datenträger-Cache, sodass eine abgeschlossene Synchronisierung einen Stromausfall übersteht.",
+ "Installiert eine Überwachung in /etc/cron.d/log2ram-auto-sync, die alle 10 Minuten läuft. Überschreitet /var/log 85 % der RAM-Disk, rotiert sie die Logs, komprimiert die gerade rotierten und schreibt alles auf den Datenträger. Kein Log wird geleert.",
+ "Hält das systemd-Journal mit einer Grenze von 1 GB auf dem Datenträger, in /etc/systemd/journald.conf.d/proxmenux-log2ram.conf. Auf einem Host mit Proxmox Backup Server kommt /etc/logrotate.d/proxmox-backup-api (20 MB × 3 Rotationen) für dessen API-Logs hinzu.",
+ "Registriert sich in installed_tools.json, damit es über Optimierungen deinstallieren rückgängig gemacht werden kann. Ein Update von einer früheren Version behält Größe und Synchronisierungsintervall bei und verlegt das Journal beim nächsten Start auf den Datenträger."
],
"howUseLabel": "Wie zu verwenden:",
- "howUseBody": "Die automatisierte Variante installiert Log2RAM unbeaufsichtigt bei erkanntem SSD-/NVMe-Systemlaufwerk. Die anpassbare Variante fragt nach Größe, Intervall und Auto-Sync-Schutz. Die Abfrage nennt derzeit 90%, das erzeugte Skript nutzt jedoch 80% für Bereinigung und 92% für die oben beschriebene intensive Bereinigung.",
+ "howUseBody": "Die automatisierte Variante installiert Log2RAM unbeaufsichtigt bei erkanntem SSD-/NVMe-Systemlaufwerk. Die anpassbare Variante fragt nach Größe, Synchronisierungsintervall und ob die Überwachung aktiviert werden soll. Die Änderungen werden beim nächsten Start wirksam.",
"verifyLabel": "Überprüfen und Verwalten aus der Shell:",
- "verifyCode": "# Service state and configured timers\nsystemctl status log2ram\nsystemctl list-timers log2ram*\n\n# Current ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Force a sync now (tmpfs → disk); does NOT shrink the tmpfs\nlog2ram write\n\n# Trigger the auto-sync guard on demand (vacuum + sync when usage is high)\n/usr/local/bin/log2ram-check.sh\n\n# Current config: SIZE, mail settings, path\ncat /etc/log2ram.conf\n\n# ProxMenux-specific cron jobs\ncat /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
+ "verifyCode": "# Service state and sync timer\nsystemctl status log2ram\nsystemctl list-timers log2ram-daily.timer\n\n# Ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Folders kept on disk, and the journal size\nls -l /var/log | grep -- '->'\njournalctl --disk-usage\n\n# Write the ramdisk to disk now\nsystemctl reload log2ram\n\n# Current configuration\ncat /etc/log2ram.conf\nsystemctl cat log2ram\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
},
"zram": {
"title": "ZRAM-Swap-Profil aktivieren",
diff --git a/web/messages/de/docs/post-install/uninstall.json b/web/messages/de/docs/post-install/uninstall.json
index 46255449..3a182408 100644
--- a/web/messages/de/docs/post-install/uninstall.json
+++ b/web/messages/de/docs/post-install/uninstall.json
@@ -125,7 +125,7 @@
},
{
"tool": "Log2RAM",
- "restores": "Stoppt und deaktiviert Dienst und Timer. Entfernt Cron-Jobs, systemd-Units, Programmdateien, Konfiguration und /var/log.hdd. Entfernt auch das apt-Paket, falls darüber installiert."
+ "restores": "Deaktiviert Dienst und Timer und entfernt die ProxMenux-Konfiguration von Log2RAM. Beim nächsten Herunterfahren schreibt Log2RAM /var/log auf den Datenträger; beim nächsten Start kehren das Journal und die Task-Logs nach /var/log zurück und die Log2RAM-Dateien werden entfernt."
},
{
"tool": "Automatisches ZFS-TRIM",
diff --git a/web/messages/en/docs/post-install/automated.json b/web/messages/en/docs/post-install/automated.json
index ed1aa387..ceedf413 100644
--- a/web/messages/en/docs/post-install/automated.json
+++ b/web/messages/en/docs/post-install/automated.json
@@ -79,7 +79,7 @@
},
{
"tool": "Log2RAM (SSD-aware, auto)",
- "what": "Reduces SSD/NVMe wear by moving /var/log to a tmpfs ramdisk with periodic disk sync. Installed from the upstream project when the root disk is SSD or NVMe. Sizes the ramdisk by host RAM (128M / 256M / 512M), schedules periodic sync and an auto-sync guard that vacuums at 80% and truncates hot logs at 92% before writing. Adjusts journald limits to fit in the ramdisk. Full details on Log2RAM.",
+ "what": "Reduces SSD/NVMe wear by moving /var/log to a tmpfs ramdisk that is written to disk on a schedule. Installed from the upstream project when the root disk is SSD or NVMe. Sizes the ramdisk by host RAM (128M / 256M / 512M) and adds a guard that rotates and writes the logs when the ramdisk passes 85%. The journal and the task logs of Proxmox VE and Proxmox Backup Server stay on disk. Full details on Log2RAM.",
"category": "Storage",
"categorySlug": "storage"
},
@@ -109,7 +109,7 @@
}
],
"hardwareTitle": "Hardware-aware defaults",
- "hardwareBody": "The script reads the host hardware before applying settings. Log2RAM is only installed on SSD / NVMe root disks by default (it prompts if detection says otherwise). Log2RAM ramdisk size is picked from total RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). The journald limits are matched to the ramdisk size when Log2RAM is active, to avoid filling it.",
+ "hardwareBody": "The script reads the host hardware before applying settings. Log2RAM is only installed on SSD / NVMe root disks by default (it prompts if detection says otherwise). Log2RAM ramdisk size is picked from total RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). When Log2RAM is active, the journal stays on disk with its own limit.",
"upgrade": {
"heading": "Update and upgrade system",
"intro": "The very first step of the Automated bundle is the Proxmox upgrade. Proxmox's own upgrade guidance for a running host (within the same major version) is to run:",
diff --git a/web/messages/en/docs/post-install/optional.json b/web/messages/en/docs/post-install/optional.json
index e6aca71a..d7bd743c 100644
--- a/web/messages/en/docs/post-install/optional.json
+++ b/web/messages/en/docs/post-install/optional.json
@@ -133,25 +133,25 @@
},
"log2ram": {
"title": "Install Log2RAM (SSD/NVMe wear reduction)",
- "intro": "Log2RAM mounts /var/log on a tmpfs ramdisk and periodically syncs it to the underlying disk. Buffering frequent journald writes in RAM can reduce root SSD wear and disk I/O. Periodic sync and a clean shutdown save logs to disk, but an abrupt power loss can lose entries written since the last sync.",
+ "intro": "Log2RAM mounts /var/log on a tmpfs ramdisk and writes it to disk on a schedule, so the logs that change all the time, such as the pveproxy access log, are written to RAM instead of the root SSD. The journal and the task logs of Proxmox VE and Proxmox Backup Server stay on disk. A clean shutdown writes the ramdisk to disk; an abrupt power loss loses only the text logs written since the last sync.",
"upstreamLabel": "Upstream project:",
"upstreamUrl": "https://github.com/azlux/log2ram",
"upstreamLinkLabel": "azlux/log2ram on GitHub",
"doesLabel": "What ProxMenux does:",
"doesItems": [
"Detects whether the root disk is SSD/NVMe by reading /sys/block/<dev>/queue/rotational. On a rotational disk the Automated flow asks first before installing.",
- "Clones the upstream repository (azlux/log2ram) into /tmp/log2ram and patches its install.sh, replacing rsync -aAXv with rsync -aXv --no-acls. That adjustment avoids the set_acl: Operation not supported exit 23 failure that the vanilla script triggers when moving /var/log on filesystems without ACL support. The log2ram systemd unit is enabled after install.",
+ "Installs Log2RAM 1.7.2 from the upstream project, checking that the download is that release, and leaves its code unchanged. Its error e-mail is turned off with NOTIFICATION=false, as Proxmox VE has no mail command by default.",
"Sizes the ramdisk based on host RAM: ≤ 8 GB → 128M, ≤ 16 GB → 256M, > 16 GB → 512M. Writes the value to SIZE= in /etc/log2ram.conf.",
- "Schedules a periodic disk sync via /etc/cron.d/log2ram: every 1h / 3h / 6h according to the same RAM tier.",
- "Installs an auto-sync guard at /usr/local/bin/log2ram-check.sh, wired to /etc/cron.d/log2ram-auto-sync to run every 10 minutes. When /var/log reaches 80% of the ramdisk size the guard vacuums journald; at 92% it first runs logrotate -f /etc/logrotate.d/proxmox-backup-api when that file exists (forcing rotation of the PBS API logs, typically the largest consumer on a host running PBS) and then truncates pveproxy access/error and pveam.log before syncing. The plain log2ram write command copies tmpfs to disk but does NOT shrink the tmpfs, so this guard prevents PVE from crashing with No space left on device when logs grow uncontrolled.",
- "Detects whether proxmox-backup-server runs as a service on the host. When present, drops /etc/logrotate.d/proxmox-backup-api (20 MB × 3 rotation) and /etc/cron.hourly/proxmox-backup-logrotate, so the PBS API logs — the usual source of growth on a PBS host — stay bounded even between Log2RAM's periodic syncs. On hosts without PBS this step is a no-op.",
- "Adjusts systemd-journald limits (SystemMaxUse, RuntimeMaxUse) to fit within the ramdisk so a single burst cannot fill it.",
- "Registers itself in installed_tools.json so it can be reverted from Uninstall Optimizations."
+ "Keeps on disk the journal (/var/log/journal), the Proxmox VE task logs (/var/log/pve) and, where it is installed, the Proxmox Backup Server logs (/var/log/proxmox-backup), in /var/hdd.journal, /var/hdd.pve and /var/hdd.proxmox-backup. Each usual path becomes a link to its folder on disk. The move happens at boot, before Log2RAM starts.",
+ "Writes the ramdisk to disk with the Log2RAM timer every 1h / 3h / 6h according to the same RAM tier, and flushes the disk after each write, so a finished sync survives a power cut.",
+ "Installs a guard in /etc/cron.d/log2ram-auto-sync that runs every 10 minutes. When /var/log passes 85% of the ramdisk, it rotates the logs, compresses the ones just rotated and writes everything to disk. No log is emptied.",
+ "Keeps the systemd journal on disk with a 1 GB limit, in /etc/systemd/journald.conf.d/proxmenux-log2ram.conf. On a host running Proxmox Backup Server, adds /etc/logrotate.d/proxmox-backup-api (20 MB × 3 rotation) for its API logs.",
+ "Registers itself in installed_tools.json so it can be reverted from Uninstall Optimizations. An update from an earlier version keeps the size and sync interval, and moves the journal to disk at the next boot."
],
"howUseLabel": "How to use:",
- "howUseBody": "On the Automated flow Log2RAM installs unattended when an SSD/NVMe root is detected. The Customizable flow asks for size, sync interval and whether to enable the auto-sync guard. Its prompt currently says 90%, but the generated guard uses the 80% cleanup and 92% aggressive thresholds described above.",
+ "howUseBody": "On the Automated flow Log2RAM installs unattended when an SSD/NVMe root is detected. The Customizable flow asks for size, sync interval and whether to enable the guard. The changes take effect at the next boot.",
"verifyLabel": "Verify and manage from the shell:",
- "verifyCode": "# Service state and configured timers\nsystemctl status log2ram\nsystemctl list-timers log2ram*\n\n# Current ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Force a sync now (tmpfs → disk); does NOT shrink the tmpfs\nlog2ram write\n\n# Trigger the auto-sync guard on demand (vacuum + sync when usage is high)\n/usr/local/bin/log2ram-check.sh\n\n# Current config: SIZE, mail settings, path\ncat /etc/log2ram.conf\n\n# ProxMenux-specific cron jobs\ncat /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
+ "verifyCode": "# Service state and sync timer\nsystemctl status log2ram\nsystemctl list-timers log2ram-daily.timer\n\n# Ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Folders kept on disk, and the journal size\nls -l /var/log | grep -- '->'\njournalctl --disk-usage\n\n# Write the ramdisk to disk now\nsystemctl reload log2ram\n\n# Current configuration\ncat /etc/log2ram.conf\nsystemctl cat log2ram\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
},
"zram": {
"title": "Enable ZRAM swap profile",
diff --git a/web/messages/en/docs/post-install/uninstall.json b/web/messages/en/docs/post-install/uninstall.json
index f25407f2..798dadcf 100644
--- a/web/messages/en/docs/post-install/uninstall.json
+++ b/web/messages/en/docs/post-install/uninstall.json
@@ -125,7 +125,7 @@
},
{
"tool": "Log2RAM",
- "restores": "Stops and disables the service and timer. Purges cron jobs, systemd units, binaries, config files and the /var/log.hdd directory. Also uninstalls the apt package if it was installed that way."
+ "restores": "Disables the service and timer and removes the ProxMenux configuration of Log2RAM. At the next shutdown Log2RAM writes /var/log to disk; at the next boot the journal and the task logs return to /var/log and the Log2RAM files are removed."
},
{
"tool": "ZFS autotrim",
diff --git a/web/messages/es/docs/post-install/automated.json b/web/messages/es/docs/post-install/automated.json
index 432263c5..1a1afca4 100644
--- a/web/messages/es/docs/post-install/automated.json
+++ b/web/messages/es/docs/post-install/automated.json
@@ -79,7 +79,7 @@
},
{
"tool": "Log2RAM (según el SSD, automático)",
- "what": "Reduce el desgaste del SSD/NVMe moviendo /var/log a una ramdisk tmpfs con sync periódico al disco. Se instala desde el proyecto oficial cuando el disco raíz es SSD o NVMe. Dimensiona la ramdisk según la RAM (128M / 256M / 512M), programa el sync periódico y un guardián de auto-sync que compacta al 80% y trunca los logs calientes al 92% antes de escribir. Ajusta los límites de journald para caber en la ramdisk. Detalle completo en Log2RAM.",
+ "what": "Reduce el desgaste del SSD/NVMe moviendo /var/log a una ramdisk tmpfs que se escribe en disco de forma programada. Se instala desde el proyecto oficial cuando el disco raíz es SSD o NVMe. Dimensiona la ramdisk según la RAM del host (128M / 256M / 512M) y añade una vigilancia que rota y escribe los logs cuando la ramdisk supera el 85%. El journal y los registros de tareas de Proxmox VE y Proxmox Backup Server se quedan en disco. Todos los detalles en Log2RAM.",
"category": "Almacenamiento",
"categorySlug": "storage"
},
@@ -109,7 +109,7 @@
}
],
"hardwareTitle": "Valores predeterminados adaptados al hardware",
- "hardwareBody": "El script lee el hardware del host antes de aplicar los ajustes. Log2RAM solo se instala en discos raíz SSD / NVMe por defecto (pregunta si la detección dice lo contrario). El tamaño del ramdisk de Log2RAM se elige a partir de la RAM total (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). Los límites de journald se ajustan al tamaño del ramdisk cuando Log2RAM está activo, para no llenarlo.",
+ "hardwareBody": "El script lee el hardware del host antes de aplicar los ajustes. Log2RAM solo se instala en discos raíz SSD / NVMe por defecto (pregunta si la detección dice lo contrario). El tamaño del ramdisk de Log2RAM se elige a partir de la RAM total (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). Con Log2RAM activo, el journal se queda en disco con su propio límite.",
"upgrade": {
"heading": "Actualizar y hacer upgrade del sistema",
"intro": "El primer paso del bundle Automatizado es el upgrade de Proxmox. La guía oficial de Proxmox para hacer upgrade de un host en ejecución (dentro de la misma versión mayor) es ejecutar:",
diff --git a/web/messages/es/docs/post-install/optional.json b/web/messages/es/docs/post-install/optional.json
index 1d7dc558..188add6a 100644
--- a/web/messages/es/docs/post-install/optional.json
+++ b/web/messages/es/docs/post-install/optional.json
@@ -133,25 +133,25 @@
},
"log2ram": {
"title": "Instalar Log2RAM (reducción de desgaste en SSD/NVMe)",
- "intro": "Log2RAM monta /var/log en una ramdisk tmpfs y lo sincroniza periódicamente al disco. Almacenar temporalmente en RAM las escrituras frecuentes de journald puede reducir el desgaste del SSD y la E/S. La sincronización y un apagado limpio guardan los registros, pero un corte brusco puede perder las entradas posteriores a la última sincronización.",
+ "intro": "Log2RAM monta /var/log en una ramdisk tmpfs y la escribe en disco de forma programada, de modo que los registros que cambian constantemente, como el log de accesos de pveproxy, se escriben en RAM en lugar de en el SSD raíz. El journal y los registros de tareas de Proxmox VE y Proxmox Backup Server se quedan en disco. Un apagado limpio escribe la ramdisk en disco; un corte de luz brusco solo pierde los logs de texto escritos desde la última sincronización.",
"upstreamLabel": "Proyecto oficial:",
"upstreamUrl": "https://github.com/azlux/log2ram",
"upstreamLinkLabel": "azlux/log2ram en GitHub",
"doesLabel": "Qué hace ProxMenux:",
"doesItems": [
"Detecta si el disco raíz es SSD/NVMe leyendo /sys/block/<dev>/queue/rotational. En un disco rotacional el flujo Automatizado pregunta antes de instalar.",
- "Clona el repositorio oficial (azlux/log2ram) en /tmp/log2ram y aplica un patch a su install.sh, sustituyendo rsync -aAXv por rsync -aXv --no-acls. Ese ajuste evita el fallo set_acl: Operation not supported con exit 23 que el script vanilla produce al mover /var/log en sistemas de archivos sin soporte de ACLs. La unit systemd log2ram se habilita después de la instalación.",
+ "Instala Log2RAM 1.7.2 desde el proyecto oficial, comprobando que lo descargado es esa versión, y deja su código sin modificar. Su correo de error se desactiva con NOTIFICATION=false, ya que Proxmox VE no incluye un comando de correo.",
"Dimensiona la ramdisk según la RAM del host: ≤ 8 GB → 128M, ≤ 16 GB → 256M, > 16 GB → 512M. Escribe el valor en SIZE= de /etc/log2ram.conf.",
- "Programa una sincronización periódica a disco vía /etc/cron.d/log2ram: cada 1h / 3h / 6h según el mismo tramo de RAM.",
- "Instala un guardián de auto-sync en /usr/local/bin/log2ram-check.sh, conectado a /etc/cron.d/log2ram-auto-sync para ejecutarse cada 10 minutos. Cuando /var/log alcanza el 80% del tamaño de la ramdisk el guardián compacta journald; al 92% primero ejecuta logrotate -f /etc/logrotate.d/proxmox-backup-api si ese archivo existe (para forzar la rotación de los logs del API de PBS, que suelen ser los que más crecen en un host con PBS) y a continuación trunca pveproxy access/error y pveam.log antes de sincronizar. El comando log2ram write por sí solo copia tmpfs a disco pero NO reduce la tmpfs, así que este guardián evita que PVE caiga con No space left on device cuando los logs crecen sin control.",
- "Detecta si proxmox-backup-server corre como servicio en el host. Cuando está presente, deposita /etc/logrotate.d/proxmox-backup-api (rotación de 20 MB × 3) y /etc/cron.hourly/proxmox-backup-logrotate, de forma que los logs del API de PBS — el origen habitual del crecimiento en un host con PBS — queden acotados incluso entre las sincronizaciones periódicas de Log2RAM. En hosts sin PBS este paso es un no-op.",
- "Ajusta los límites de systemd-journald (SystemMaxUse, RuntimeMaxUse) para que quepan en la ramdisk y así una ráfaga puntual no la llene.",
- "Se registra en installed_tools.json para poder revertirlo desde Desinstalar optimizaciones."
+ "Mantiene en disco el journal (/var/log/journal), los registros de tareas de Proxmox VE (/var/log/pve) y, si está instalado, los de Proxmox Backup Server (/var/log/proxmox-backup), en /var/hdd.journal, /var/hdd.pve y /var/hdd.proxmox-backup. Cada ruta habitual pasa a ser un enlace a su carpeta en disco. El traslado se hace al arrancar, antes de que se inicie Log2RAM.",
+ "Escribe la ramdisk en disco con el temporizador de Log2RAM cada 1h / 3h / 6h según el mismo tramo de RAM, y vuelca el disco después de cada escritura, de modo que una sincronización terminada sobrevive a un corte de luz.",
+ "Instala una vigilancia en /etc/cron.d/log2ram-auto-sync que se ejecuta cada 10 minutos. Cuando /var/log supera el 85% de la ramdisk, rota los logs, comprime los recién rotados y escribe todo en disco. No vacía ningún log.",
+ "Mantiene el journal de systemd en disco con un límite de 1 GB, en /etc/systemd/journald.conf.d/proxmenux-log2ram.conf. En un host con Proxmox Backup Server, añade /etc/logrotate.d/proxmox-backup-api (rotación de 20 MB × 3) para sus logs de la API.",
+ "Se registra en installed_tools.json para poder revertirse desde Desinstalar optimizaciones. Una actualización desde una versión anterior conserva el tamaño y el intervalo de sincronización, y lleva el journal a disco en el siguiente arranque."
],
"howUseLabel": "Cómo se usa:",
- "howUseBody": "En el flujo automatizado Log2RAM se instala sin intervención al detectar un disco raíz SSD/NVMe. El personalizable pregunta por tamaño, intervalo y activación del auxiliar de sincronización. El mensaje indica 90%, pero el auxiliar generado usa los umbrales de limpieza del 80% y actuación intensiva del 92% descritos arriba.",
+ "howUseBody": "En el flujo automatizado Log2RAM se instala sin intervención al detectar un disco raíz SSD/NVMe. El personalizable pregunta por el tamaño, el intervalo de sincronización y si activar la vigilancia. Los cambios se aplican en el siguiente arranque.",
"verifyLabel": "Verificar y gestionar desde la shell:",
- "verifyCode": "# Service state and configured timers\nsystemctl status log2ram\nsystemctl list-timers log2ram*\n\n# Current ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Force a sync now (tmpfs → disk); does NOT shrink the tmpfs\nlog2ram write\n\n# Trigger the auto-sync guard on demand (vacuum + sync when usage is high)\n/usr/local/bin/log2ram-check.sh\n\n# Current config: SIZE, mail settings, path\ncat /etc/log2ram.conf\n\n# ProxMenux-specific cron jobs\ncat /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
+ "verifyCode": "# Service state and sync timer\nsystemctl status log2ram\nsystemctl list-timers log2ram-daily.timer\n\n# Ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Folders kept on disk, and the journal size\nls -l /var/log | grep -- '->'\njournalctl --disk-usage\n\n# Write the ramdisk to disk now\nsystemctl reload log2ram\n\n# Current configuration\ncat /etc/log2ram.conf\nsystemctl cat log2ram\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
},
"zram": {
"title": "Activar el perfil de swap con ZRAM",
diff --git a/web/messages/es/docs/post-install/uninstall.json b/web/messages/es/docs/post-install/uninstall.json
index 32311b09..f4b20a44 100644
--- a/web/messages/es/docs/post-install/uninstall.json
+++ b/web/messages/es/docs/post-install/uninstall.json
@@ -125,7 +125,7 @@
},
{
"tool": "Log2RAM",
- "restores": "Para y desactiva el servicio y el timer. Purga cron jobs, units systemd, binarios, archivos de configuración y el directorio /var/log.hdd. También desinstala el paquete apt si se instaló por esa vía."
+ "restores": "Desactiva el servicio y el temporizador y elimina la configuración de ProxMenux para Log2RAM. En el siguiente apagado Log2RAM escribe /var/log en disco; en el siguiente arranque el journal y los registros de tareas vuelven a /var/log y se eliminan los ficheros de Log2RAM."
},
{
"tool": "TRIM automático de ZFS",
diff --git a/web/messages/fr/docs/post-install/automated.json b/web/messages/fr/docs/post-install/automated.json
index 08183f4a..98ff018a 100644
--- a/web/messages/fr/docs/post-install/automated.json
+++ b/web/messages/fr/docs/post-install/automated.json
@@ -79,7 +79,7 @@
},
{
"tool": "Log2RAM (adapté aux SSD, automatique)",
- "what": "Réduit l’usure SSD/NVMe en déplaçant /var/log dans un disque RAM tmpfs synchronisé régulièrement vers le disque. Installation depuis le projet amont si le disque racine est SSD ou NVMe. Taille selon la RAM de l’hôte (128M / 256M / 512M), synchronisation périodique et protection automatique qui purge les anciens journaux à 80% et tronque les journaux très actifs à 92% avant écriture. Adapte les limites journald au disque RAM. Détails dans Log2RAM.",
+ "what": "Réduit l'usure du SSD/NVMe en déplaçant /var/log vers un ramdisk tmpfs écrit sur le disque selon un calendrier. Installé depuis le projet officiel lorsque le disque système est un SSD ou un NVMe. Dimensionne le ramdisk selon la RAM de l'hôte (128M / 256M / 512M) et ajoute une surveillance qui fait tourner et écrit les journaux lorsque le ramdisk dépasse 85 %. Le journal et les journaux de tâches de Proxmox VE et de Proxmox Backup Server restent sur le disque. Tous les détails dans Log2RAM.",
"category": "Stockage",
"categorySlug": "storage"
},
@@ -109,7 +109,7 @@
}
],
"hardwareTitle": "Valeurs par défaut adaptées au matériel",
- "hardwareBody": "Le script examine le matériel avant de configurer l’hôte. Par défaut, Log2RAM s’installe uniquement si le disque racine est SSD ou NVMe ; il demande confirmation sinon. La taille du disque RAM dépend de la RAM totale (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). Quand Log2RAM est actif, les limites journald sont adaptées au disque RAM pour éviter de le remplir.",
+ "hardwareBody": "Le script examine le matériel avant de configurer l’hôte. Par défaut, Log2RAM s’installe uniquement si le disque racine est SSD ou NVMe ; il demande confirmation sinon. La taille du disque RAM dépend de la RAM totale (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). Lorsque Log2RAM est actif, le journal reste sur le disque avec sa propre limite.",
"upgrade": {
"heading": "Mettre à jour les listes de paquets et le système",
"intro": "La première étape du parcours automatisé est la mise à niveau Proxmox. Pour un hôte en fonctionnement au sein de la même version majeure, Proxmox recommande :",
diff --git a/web/messages/fr/docs/post-install/optional.json b/web/messages/fr/docs/post-install/optional.json
index c4f06e38..91b28b65 100644
--- a/web/messages/fr/docs/post-install/optional.json
+++ b/web/messages/fr/docs/post-install/optional.json
@@ -133,25 +133,25 @@
},
"log2ram": {
"title": "Installer Log2RAM (SSD/NVMe réduction de l'usure)",
- "intro": "Log2RAM monte /var/log sur un disque RAM tmpfs et le synchronise régulièrement vers le disque. Mettre en mémoire les écritures journald fréquentes peut réduire l’usure SSD et les E/S. La synchronisation et un arrêt propre sauvegardent les journaux ; une coupure brutale peut perdre les entrées depuis la dernière synchronisation.",
+ "intro": "Log2RAM monte /var/log sur un ramdisk tmpfs et l'écrit sur le disque selon un calendrier, de sorte que les journaux qui changent en permanence, comme le journal d'accès de pveproxy, sont écrits en RAM plutôt que sur le SSD système. Le journal et les journaux de tâches de Proxmox VE et de Proxmox Backup Server restent sur le disque. Un arrêt propre écrit le ramdisk sur le disque ; une coupure de courant brutale ne fait perdre que les journaux texte écrits depuis la dernière synchronisation.",
"upstreamLabel": "Projet en amont:",
"upstreamUrl": "https://github.com/azlux/log2ram",
"upstreamLinkLabel": "azlux/log2ram sur GitHub",
"doesLabel": "Que fait ProxMenux:",
"doesItems": [
"Détecte si le disque racine est SSD/NVMe en lisant /sys/block/<dev>/queue/rotational. Sur un disque rotatif, le flux automatisé demande d'abord avant d'installer.",
- "Clone le dépôt original (azlux/log2ram) dans /tmp/log2ram et modifie install.sh en remplaçant rsync -aAXv par rsync -aXv --no-acls. Cela évite l’erreur set_acl: Operation not supported avec code de sortie 23, déclenchée par le script original lors du déplacement de /var/log sur un système sans ACL. L’unité systemd log2ram est activée après installation.",
+ "Installe Log2RAM 1.7.2 depuis le projet officiel, en vérifiant que le téléchargement correspond à cette version, sans modifier son code. Son e-mail d'erreur est désactivé avec NOTIFICATION=false, car Proxmox VE n'inclut pas de commande mail.",
"Dimensionne le disque RAM selon la mémoire de l’hôte : ≤ 8 GB → 128M, ≤ 16 GB → 256M, > 16 GB → 512M. Écrit la valeur dans SIZE= de /etc/log2ram.conf.",
- "Planifie une synchronisation périodique du disque via /etc/cron.d/log2ram : toutes les 1h / 3h / 6h selon le même niveau RAM.",
- "Installe /usr/local/bin/log2ram-check.sh, lancé toutes les 10 minutes par /etc/cron.d/log2ram-auto-sync. Quand /var/log atteint 80% du disque RAM, il purge les anciens journaux journald. À 92%, il lance d’abord logrotate -f /etc/logrotate.d/proxmox-backup-api si le fichier existe : les journaux API PBS occupent souvent le plus d’espace sur un hôte PBS. Il tronque ensuite pveproxy access/error et pveam.log avant synchronisation. log2ram write seul copie tmpfs sur disque sans le réduire. Cette protection aide à éviter un arrêt de PVE avec No space left on device lorsque les journaux grossissent sans contrôle.",
- "Détecte le service proxmox-backup-server sur l’hôte. S’il est présent, crée /etc/logrotate.d/proxmox-backup-api (20 Mo × 3 rotations) et /etc/cron.hourly/proxmox-backup-logrotate pour limiter les journaux API PBS, souvent à l’origine de la croissance, même entre les synchronisations Log2RAM. Sans PBS, cette étape ne change rien.",
- "Ajuste les limites systemd-journald (SystemMaxUse, RuntimeMaxUse) pour qu'elles s'adaptent au disque virtuel afin qu'une seule rafale ne puisse pas le remplir.",
- "S’enregistre dans installed_tools.json pour pouvoir être annulé via Désinstaller les optimisations."
+ "Conserve sur le disque le journal (/var/log/journal), les journaux de tâches de Proxmox VE (/var/log/pve) et, s'il est installé, ceux de Proxmox Backup Server (/var/log/proxmox-backup), dans /var/hdd.journal, /var/hdd.pve et /var/hdd.proxmox-backup. Chaque chemin habituel devient un lien vers son dossier sur le disque. Le déplacement a lieu au démarrage, avant que Log2RAM ne démarre.",
+ "Écrit le ramdisk sur le disque avec le minuteur de Log2RAM toutes les 1 h / 3 h / 6 h selon le même palier de RAM, et vide le cache disque après chaque écriture, de sorte qu'une synchronisation terminée résiste à une coupure de courant.",
+ "Installe une surveillance dans /etc/cron.d/log2ram-auto-sync exécutée toutes les 10 minutes. Lorsque /var/log dépasse 85 % du ramdisk, elle effectue la rotation des journaux, compresse ceux qui viennent d'être tournés et écrit le tout sur le disque. Aucun journal n'est vidé.",
+ "Conserve le journal systemd sur le disque avec une limite de 1 Go, dans /etc/systemd/journald.conf.d/proxmenux-log2ram.conf. Sur un hôte exécutant Proxmox Backup Server, ajoute /etc/logrotate.d/proxmox-backup-api (rotation 20 Mo × 3) pour ses journaux d'API.",
+ "S'enregistre dans installed_tools.json afin de pouvoir être annulé depuis Désinstaller les optimisations. Une mise à jour depuis une version antérieure conserve la taille et l'intervalle de synchronisation, et déplace le journal sur le disque au démarrage suivant."
],
"howUseLabel": "Comment utiliser:",
- "howUseBody": "Le parcours automatisé installe Log2RAM sans intervention si le disque racine est SSD/NVMe. Le parcours personnalisable demande taille, intervalle et activation de la protection. L’invite indique actuellement 90%, mais le script généré utilise les seuils de nettoyage à 80% et intensif à 92% décrits plus haut.",
+ "howUseBody": "Dans le flux automatisé, Log2RAM s'installe sans intervention lorsqu'un disque système SSD/NVMe est détecté. Le flux personnalisable demande la taille, l'intervalle de synchronisation et s'il faut activer la surveillance. Les changements prennent effet au démarrage suivant.",
"verifyLabel": "Vérifier et gérer à partir du shell:",
- "verifyCode": "# Service state and configured timers\nsystemctl status log2ram\nsystemctl list-timers log2ram*\n\n# Current ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Force a sync now (tmpfs → disk); does NOT shrink the tmpfs\nlog2ram write\n\n# Trigger the auto-sync guard on demand (vacuum + sync when usage is high)\n/usr/local/bin/log2ram-check.sh\n\n# Current config: SIZE, mail settings, path\ncat /etc/log2ram.conf\n\n# ProxMenux-specific cron jobs\ncat /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
+ "verifyCode": "# Service state and sync timer\nsystemctl status log2ram\nsystemctl list-timers log2ram-daily.timer\n\n# Ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Folders kept on disk, and the journal size\nls -l /var/log | grep -- '->'\njournalctl --disk-usage\n\n# Write the ramdisk to disk now\nsystemctl reload log2ram\n\n# Current configuration\ncat /etc/log2ram.conf\nsystemctl cat log2ram\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
},
"zram": {
"title": "Activer le profil d'échange ZRAM",
diff --git a/web/messages/fr/docs/post-install/uninstall.json b/web/messages/fr/docs/post-install/uninstall.json
index b61db8ae..2b43cc21 100644
--- a/web/messages/fr/docs/post-install/uninstall.json
+++ b/web/messages/fr/docs/post-install/uninstall.json
@@ -125,7 +125,7 @@
},
{
"tool": "Log2RAM",
- "restores": "Arrête et désactive le service et le minuteur. Supprime tâches cron, unités systemd, binaires, configuration et /var/log.hdd. Désinstalle aussi le paquet apt si cette méthode a été utilisée."
+ "restores": "Désactive le service et le minuteur et supprime la configuration ProxMenux de Log2RAM. À l'arrêt suivant, Log2RAM écrit /var/log sur le disque ; au démarrage suivant, le journal et les journaux de tâches reviennent dans /var/log et les fichiers de Log2RAM sont supprimés."
},
{
"tool": "TRIM automatique ZFS",
diff --git a/web/messages/it/docs/post-install/automated.json b/web/messages/it/docs/post-install/automated.json
index c41c700a..65ee6916 100644
--- a/web/messages/it/docs/post-install/automated.json
+++ b/web/messages/it/docs/post-install/automated.json
@@ -79,7 +79,7 @@
},
{
"tool": "Log2RAM (ottimizzato per SSD, automatico)",
- "what": "Riduce l’usura SSD/NVMe spostando /var/log in un ramdisk tmpfs sincronizzato periodicamente sul disco. Installazione dal progetto upstream quando il disco root è SSD o NVMe. Dimensione in base alla RAM dell’host (128M / 256M / 512M), sincronizzazione periodica e protezione automatica che elimina vecchi journal all’80% e tronca i log molto attivi al 92% prima della scrittura. Adatta i limiti journald al ramdisk. Dettagli su Log2RAM.",
+ "what": "Riduce l'usura di SSD/NVMe spostando /var/log su un ramdisk tmpfs che viene scritto su disco a intervalli programmati. Viene installato dal progetto ufficiale quando il disco di sistema è SSD o NVMe. Dimensiona il ramdisk in base alla RAM dell'host (128M / 256M / 512M) e aggiunge un controllo che ruota e scrive i log quando il ramdisk supera l'85%. Il journal e i log delle attività di Proxmox VE e Proxmox Backup Server restano su disco. Tutti i dettagli in Log2RAM.",
"category": "Archiviazione",
"categorySlug": "storage"
},
@@ -109,7 +109,7 @@
}
],
"hardwareTitle": "Impostazioni predefinite in base all’hardware",
- "hardwareBody": "Lo script legge l'hardware dell'host prima di applicare le impostazioni. Log2RAM è installato solo su dischi SSD/root NVMe per impostazione predefinita (richiede se il rilevamento indica diversamente). La dimensione del disco ram Log2RAM viene scelta dal totale RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). I limiti del journald vengono adattati alla dimensione del ramdisk quando Log2RAM è attivo, per evitare di riempirlo.",
+ "hardwareBody": "Lo script legge l'hardware dell'host prima di applicare le impostazioni. Log2RAM è installato solo su dischi SSD/root NVMe per impostazione predefinita (richiede se il rilevamento indica diversamente). La dimensione del disco ram Log2RAM viene scelta dal totale RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). Con Log2RAM attivo, il journal resta su disco con un proprio limite.",
"upgrade": {
"heading": "Aggiorna gli elenchi dei pacchetti e il sistema",
"intro": "Il primo passo del pacchetto automatizzato è l'aggiornamento Proxmox. La guida di aggiornamento di Proxmox per un host in esecuzione (nella stessa versione principale) è quella di eseguire:",
diff --git a/web/messages/it/docs/post-install/optional.json b/web/messages/it/docs/post-install/optional.json
index 576b0b13..94d471c7 100644
--- a/web/messages/it/docs/post-install/optional.json
+++ b/web/messages/it/docs/post-install/optional.json
@@ -133,25 +133,25 @@
},
"log2ram": {
"title": "Installare Log2RAM (SSD/NVMe riduzione dell'usura)",
- "intro": "Log2RAM monta /var/log su un ramdisk tmpfs e lo sincronizza periodicamente sul disco. Accumulare in RAM le frequenti scritture journald può ridurre usura SSD e I/O. Sincronizzazione e arresto regolare salvano i log, ma una perdita improvvisa di alimentazione può perdere le voci dall’ultima sincronizzazione.",
+ "intro": "Log2RAM monta /var/log su un ramdisk tmpfs e lo scrive su disco a intervalli programmati, così i log che cambiano di continuo, come il log degli accessi di pveproxy, vengono scritti in RAM invece che sull'SSD di sistema. Il journal e i log delle attività di Proxmox VE e Proxmox Backup Server restano su disco. Uno spegnimento pulito scrive il ramdisk su disco; un'interruzione di corrente improvvisa fa perdere solo i log di testo scritti dopo l'ultima sincronizzazione.",
"upstreamLabel": "Progetto a monte:",
"upstreamUrl": "https://github.com/azlux/log2ram",
"upstreamLinkLabel": "azlux/log2ram su GitHub",
"doesLabel": "Cosa fa ProxMenux:",
"doesItems": [
"Rileva se il disco root è SSD/NVMe leggendo /sys/block/<dev>/queue/rotational. Su un disco rotazionale il flusso automatizzato chiede prima di installare.",
- "Clona il repository originale (azlux/log2ram) in /tmp/log2ram e modifica install.sh, sostituendo rsync -aAXv con rsync -aXv --no-acls. Evita l’errore set_acl: Operation not supported con codice di uscita 23, che lo script originale genera spostando /var/log su filesystem senza ACL. L’unità systemd log2ram viene abilitata dopo l’installazione.",
+ "Installa Log2RAM 1.7.2 dal progetto ufficiale, verificando che il download sia quella versione, e lascia il suo codice invariato. L'email di errore viene disattivata con NOTIFICATION=false, poiché Proxmox VE non include un comando mail.",
"Dimensiona il ramdisk in base alla RAM dell’host: ≤ 8 GB → 128M, ≤ 16 GB → 256M, > 16 GB → 512M. Scrive il valore in SIZE= di /etc/log2ram.conf.",
- "Pianifica una sincronizzazione periodica del disco tramite /etc/cron.d/log2ram: ogni 1h / 3h / 6h secondo lo stesso livello RAM.",
- "Installa /usr/local/bin/log2ram-check.sh, eseguito ogni 10 minuti da /etc/cron.d/log2ram-auto-sync. Quando /var/log raggiunge l’80% del ramdisk, pulisce i vecchi journal. Al 92% esegue prima logrotate -f /etc/logrotate.d/proxmox-backup-api se il file esiste: i log API PBS sono spesso i maggiori consumatori di spazio su un host PBS. Poi tronca pveproxy access/error e pveam.log prima della sincronizzazione. log2ram write da solo copia tmpfs sul disco senza ridurlo. La protezione aiuta a evitare arresti di PVE con No space left on device per crescita incontrollata dei log.",
- "Rileva il servizio proxmox-backup-server sull’host. Se presente, crea /etc/logrotate.d/proxmox-backup-api (20 MB × 3 rotazioni) e /etc/cron.hourly/proxmox-backup-logrotate per limitare i log API PBS, abituale fonte di crescita, anche tra le sincronizzazioni Log2RAM. Senza PBS non modifica nulla.",
- "Regola i limiti systemd-journald (SystemMaxUse, RuntimeMaxUse) per adattarsi all'interno del ramdisk in modo che un singolo scoppio non possa riempirlo.",
- "Si registra in installed_tools.json per consentire il ripristino da Disinstalla ottimizzazioni."
+ "Mantiene su disco il journal (/var/log/journal), i log delle attività di Proxmox VE (/var/log/pve) e, se installato, quelli di Proxmox Backup Server (/var/log/proxmox-backup), in /var/hdd.journal, /var/hdd.pve e /var/hdd.proxmox-backup. Ogni percorso abituale diventa un collegamento alla sua cartella su disco. Lo spostamento avviene all'avvio, prima che parta Log2RAM.",
+ "Scrive il ramdisk su disco con il timer di Log2RAM ogni 1h / 3h / 6h secondo la stessa fascia di RAM, e svuota la cache del disco dopo ogni scrittura, così una sincronizzazione completata sopravvive a un'interruzione di corrente.",
+ "Installa un controllo in /etc/cron.d/log2ram-auto-sync eseguito ogni 10 minuti. Quando /var/log supera l'85% del ramdisk, ruota i log, comprime quelli appena ruotati e scrive tutto su disco. Nessun log viene svuotato.",
+ "Mantiene il journal di systemd su disco con un limite di 1 GB, in /etc/systemd/journald.conf.d/proxmenux-log2ram.conf. Su un host con Proxmox Backup Server aggiunge /etc/logrotate.d/proxmox-backup-api (rotazione 20 MB × 3) per i suoi log delle API.",
+ "Si registra in installed_tools.json per poter essere annullato da Disinstalla ottimizzazioni. Un aggiornamento da una versione precedente mantiene la dimensione e l'intervallo di sincronizzazione e sposta il journal su disco al successivo avvio."
],
"howUseLabel": "Come usare:",
- "howUseBody": "Il percorso automatizzato installa Log2RAM senza intervento se rileva un disco root SSD/NVMe. Quello personalizzabile chiede dimensione, intervallo e attivazione della protezione. Il messaggio indica 90%, ma lo script generato usa le soglie di pulizia all’80% e intensiva al 92% descritte sopra.",
+ "howUseBody": "Nel flusso automatico Log2RAM si installa senza intervento quando rileva un disco di sistema SSD/NVMe. Il flusso personalizzabile chiede la dimensione, l'intervallo di sincronizzazione e se attivare il controllo. Le modifiche hanno effetto al successivo avvio.",
"verifyLabel": "Verificare e gestire dalla shell:",
- "verifyCode": "# Service state and configured timers\nsystemctl status log2ram\nsystemctl list-timers log2ram*\n\n# Current ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Force a sync now (tmpfs → disk); does NOT shrink the tmpfs\nlog2ram write\n\n# Trigger the auto-sync guard on demand (vacuum + sync when usage is high)\n/usr/local/bin/log2ram-check.sh\n\n# Current config: SIZE, mail settings, path\ncat /etc/log2ram.conf\n\n# ProxMenux-specific cron jobs\ncat /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
+ "verifyCode": "# Service state and sync timer\nsystemctl status log2ram\nsystemctl list-timers log2ram-daily.timer\n\n# Ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Folders kept on disk, and the journal size\nls -l /var/log | grep -- '->'\njournalctl --disk-usage\n\n# Write the ramdisk to disk now\nsystemctl reload log2ram\n\n# Current configuration\ncat /etc/log2ram.conf\nsystemctl cat log2ram\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
},
"zram": {
"title": "Abilita il profilo di scambio ZRAM",
diff --git a/web/messages/it/docs/post-install/uninstall.json b/web/messages/it/docs/post-install/uninstall.json
index e0ffc8d6..075967fe 100644
--- a/web/messages/it/docs/post-install/uninstall.json
+++ b/web/messages/it/docs/post-install/uninstall.json
@@ -125,7 +125,7 @@
},
{
"tool": "Log2RAM",
- "restores": "Ferma e disabilita servizio e timer. Elimina processi cron, unità systemd, binari, configurazione e /var/log.hdd. Rimuove anche il pacchetto apt se installato in quel modo."
+ "restores": "Disattiva il servizio e il timer e rimuove la configurazione di ProxMenux per Log2RAM. Al successivo spegnimento Log2RAM scrive /var/log su disco; al successivo avvio il journal e i log delle attività tornano in /var/log e i file di Log2RAM vengono rimossi."
},
{
"tool": "TRIM automatico ZFS",
diff --git a/web/messages/pt/docs/post-install/automated.json b/web/messages/pt/docs/post-install/automated.json
index 96ad54ff..5bb6e11e 100644
--- a/web/messages/pt/docs/post-install/automated.json
+++ b/web/messages/pt/docs/post-install/automated.json
@@ -79,7 +79,7 @@
},
{
"tool": "Log2RAM (adaptado a SSD, automático)",
- "what": "Reduz o desgaste SSD/NVMe movendo /var/log para um ramdisk tmpfs com sincronização periódica para disco. Instalação a partir do projeto original quando o disco raiz é SSD ou NVMe. Dimensiona pela RAM do host (128M / 256M / 512M), agenda sincronização periódica e proteção automática que limpa journals antigos aos 80% e trunca registos muito ativos aos 92% antes de gravar. Ajusta limites journald ao ramdisk. Detalhes em Log2RAM.",
+ "what": "Reduz o desgaste do SSD/NVMe movendo /var/log para um ramdisk tmpfs gravado no disco de forma programada. É instalado a partir do projeto oficial quando o disco do sistema é SSD ou NVMe. Dimensiona o ramdisk segundo a RAM do host (128M / 256M / 512M) e adiciona uma vigilância que rotaciona e grava os logs quando o ramdisk ultrapassa 85%. O journal e os logs de tarefas do Proxmox VE e do Proxmox Backup Server ficam no disco. Todos os detalhes em Log2RAM.",
"category": "Armazenamento",
"categorySlug": "storage"
},
@@ -109,7 +109,7 @@
}
],
"hardwareTitle": "Predefinições adaptadas ao hardware",
- "hardwareBody": "O script lê o hardware do host antes de aplicar as configurações. Log2RAM é instalado apenas em discos raiz SSD / NVMe por padrão (ele avisa se a detecção indicar o contrário). O tamanho do ramdisk Log2RAM é escolhido do total RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). Os limites journald correspondem ao tamanho do disco RAM quando Log2RAM está ativo, para evitar preenchê-lo.",
+ "hardwareBody": "O script lê o hardware do host antes de aplicar as configurações. Log2RAM é instalado apenas em discos raiz SSD / NVMe por padrão (ele avisa se a detecção indicar o contrário). O tamanho do ramdisk Log2RAM é escolhido do total RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). Com o Log2RAM ativo, o journal fica no disco com o seu próprio limite.",
"upgrade": {
"heading": "Atualizar listas de pacotes e sistema",
"intro": "O primeiro passo do percurso automatizado é atualizar o Proxmox. Para um host em funcionamento dentro da mesma versão principal, o Proxmox recomenda:",
diff --git a/web/messages/pt/docs/post-install/optional.json b/web/messages/pt/docs/post-install/optional.json
index 923883d7..4c4124b1 100644
--- a/web/messages/pt/docs/post-install/optional.json
+++ b/web/messages/pt/docs/post-install/optional.json
@@ -133,25 +133,25 @@
},
"log2ram": {
"title": "Instalar Log2RAM (redução do desgaste de SSD/NVMe)",
- "intro": "Log2RAM monta /var/log num ramdisk tmpfs e sincroniza-o periodicamente para disco. Guardar temporariamente na RAM as escritas frequentes do journald pode reduzir desgaste SSD e E/S. A sincronização e o encerramento normal guardam os registos; um corte abrupto pode perder entradas desde a última sincronização.",
+ "intro": "O Log2RAM monta /var/log num ramdisk tmpfs e grava-o no disco de forma programada, para que os logs que mudam constantemente, como o log de acessos do pveproxy, sejam gravados na RAM em vez do SSD do sistema. O journal e os logs de tarefas do Proxmox VE e do Proxmox Backup Server ficam no disco. Um desligamento limpo grava o ramdisk no disco; uma queda de energia abrupta perde apenas os logs de texto gravados desde a última sincronização.",
"upstreamLabel": "Projeto a montante:",
"upstreamUrl": "https://github.com/azlux/log2ram",
"upstreamLinkLabel": "azlux/log2ram em GitHub",
"doesLabel": "O que o ProxMenux faz:",
"doesItems": [
"Detecta se o disco raiz é SSD/NVMe lendo /sys/block/<dev>/queue/rotational. Em um disco rotacional o fluxo automatizado pede primeiro antes de instalar.",
- "Clona o repositório original (azlux/log2ram) para /tmp/log2ram e modifica install.sh, substituindo rsync -aAXv por rsync -aXv --no-acls. Evita o erro set_acl: Operation not supported com código de saída 23, gerado pelo script original ao mover /var/log em sistemas sem ACL. A unidade systemd log2ram é ativada após instalação.",
+ "Instala o Log2RAM 1.7.2 a partir do projeto oficial, verificando que o download é essa versão, e mantém o seu código inalterado. O e-mail de erro é desativado com NOTIFICATION=false, pois o Proxmox VE não inclui um comando de e-mail.",
"Dimensiona o ramdisk pela RAM do host: ≤ 8 GB → 128M, ≤ 16 GB → 256M, > 16 GB → 512M. Escreve o valor em SIZE= de /etc/log2ram.conf.",
- "Agenda uma sincronização periódica do disco via /etc/cron.d/log2ram: a cada 1h / 3h / 6h de acordo com o mesmo nível RAM.",
- "Instala /usr/local/bin/log2ram-check.sh, executado a cada 10 minutos por /etc/cron.d/log2ram-auto-sync. Quando /var/log chega a 80% do ramdisk, limpa journals antigos. Aos 92%, executa primeiro logrotate -f /etc/logrotate.d/proxmox-backup-api se o ficheiro existir: os registos API PBS costumam ocupar mais espaço num host PBS. Depois trunca pveproxy access/error e pveam.log antes de sincronizar. log2ram write apenas copia tmpfs para disco, sem o reduzir. A proteção ajuda a evitar falhas PVE com No space left on device por crescimento descontrolado dos registos.",
- "Deteta o serviço proxmox-backup-server no host. Se existir, cria /etc/logrotate.d/proxmox-backup-api (20 MB × 3 rotações) e /etc/cron.hourly/proxmox-backup-logrotate para limitar os registos API PBS, fonte habitual de crescimento, mesmo entre sincronizações Log2RAM. Sem PBS não altera nada.",
- "Ajusta os limites systemd-journald (SystemMaxUse, RuntimeMaxUse) ao ramdisk para que uma rajada de registos não o encha.",
- "Fica registado em installed_tools.json para poder ser revertido em Desinstalar otimizações."
+ "Mantém no disco o journal (/var/log/journal), os logs de tarefas do Proxmox VE (/var/log/pve) e, se estiver instalado, os do Proxmox Backup Server (/var/log/proxmox-backup), em /var/hdd.journal, /var/hdd.pve e /var/hdd.proxmox-backup. Cada caminho habitual passa a ser um link para a sua pasta no disco. A mudança acontece na inicialização, antes de o Log2RAM arrancar.",
+ "Grava o ramdisk no disco com o temporizador do Log2RAM a cada 1h / 3h / 6h conforme a mesma faixa de RAM, e descarrega o disco após cada gravação, para que uma sincronização concluída sobreviva a uma queda de energia.",
+ "Instala uma vigilância em /etc/cron.d/log2ram-auto-sync executada a cada 10 minutos. Quando /var/log ultrapassa 85% do ramdisk, rotaciona os logs, comprime os recém-rotacionados e grava tudo no disco. Nenhum log é esvaziado.",
+ "Mantém o journal do systemd no disco com um limite de 1 GB, em /etc/systemd/journald.conf.d/proxmenux-log2ram.conf. Num host com Proxmox Backup Server, adiciona /etc/logrotate.d/proxmox-backup-api (rotação de 20 MB × 3) para os seus logs da API.",
+ "Regista-se em installed_tools.json para poder ser revertido em Desinstalar otimizações. Uma atualização a partir de uma versão anterior mantém o tamanho e o intervalo de sincronização, e leva o journal para o disco na inicialização seguinte."
],
"howUseLabel": "Como utilizar:",
- "howUseBody": "O percurso automatizado instala Log2RAM sem intervenção se detetar disco raiz SSD/NVMe. O personalizável pergunta tamanho, intervalo e ativação da proteção. A pergunta indica 90%, mas o script gerado usa os limiares de limpeza de 80% e intensiva de 92% descritos acima.",
+ "howUseBody": "No fluxo automatizado, o Log2RAM instala-se sem intervenção ao detetar um disco do sistema SSD/NVMe. O personalizável pergunta o tamanho, o intervalo de sincronização e se deve ativar a vigilância. As alterações aplicam-se na inicialização seguinte.",
"verifyLabel": "Verificar e gerenciar a partir da shell:",
- "verifyCode": "# Service state and configured timers\nsystemctl status log2ram\nsystemctl list-timers log2ram*\n\n# Current ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Force a sync now (tmpfs → disk); does NOT shrink the tmpfs\nlog2ram write\n\n# Trigger the auto-sync guard on demand (vacuum + sync when usage is high)\n/usr/local/bin/log2ram-check.sh\n\n# Current config: SIZE, mail settings, path\ncat /etc/log2ram.conf\n\n# ProxMenux-specific cron jobs\ncat /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
+ "verifyCode": "# Service state and sync timer\nsystemctl status log2ram\nsystemctl list-timers log2ram-daily.timer\n\n# Ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Folders kept on disk, and the journal size\nls -l /var/log | grep -- '->'\njournalctl --disk-usage\n\n# Write the ramdisk to disk now\nsystemctl reload log2ram\n\n# Current configuration\ncat /etc/log2ram.conf\nsystemctl cat log2ram\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
},
"zram": {
"title": "Habilitar perfil de troca ZRAM",
diff --git a/web/messages/pt/docs/post-install/uninstall.json b/web/messages/pt/docs/post-install/uninstall.json
index 50b1b48e..4982c0e3 100644
--- a/web/messages/pt/docs/post-install/uninstall.json
+++ b/web/messages/pt/docs/post-install/uninstall.json
@@ -125,7 +125,7 @@
},
{
"tool": "Log2RAM",
- "restores": "Para e desativa o serviço e o timer. Purga trabalhos de cron, unidades systemd, binários, arquivos de configuração e o diretório /var/log.hdd. Também desinstala o pacote apt se ele foi instalado dessa forma."
+ "restores": "Desativa o serviço e o temporizador e remove a configuração do ProxMenux para o Log2RAM. No desligamento seguinte, o Log2RAM grava /var/log no disco; na inicialização seguinte, o journal e os logs de tarefas voltam para /var/log e os ficheiros do Log2RAM são removidos."
},
{
"tool": "TRIM automático do ZFS",
diff --git a/web/messages/sk/docs/post-install/automated.json b/web/messages/sk/docs/post-install/automated.json
index 39289025..373698ec 100644
--- a/web/messages/sk/docs/post-install/automated.json
+++ b/web/messages/sk/docs/post-install/automated.json
@@ -79,7 +79,7 @@
},
{
"tool": "Log2RAM (prispôsobené pre SSD, automatické)",
- "what": "Znižuje opotrebenie SSD/NVMe presunom /var/log na ramdisk tmpfs s pravidelnou synchronizáciou na disk. Inštalácia z pôvodného projektu pri koreňovom disku SSD alebo NVMe. Veľkosť podľa RAM hostiteľa (128M / 256M / 512M), pravidelná synchronizácia a ochrana, ktorá pri 80% zaplnení vyčistí staré žurnály a pri 92% skráti rýchlo rastúce logy pred zápisom. Limity journald prispôsobí ramdisku. Podrobnosti v Log2RAM.",
+ "what": "Znižuje opotrebenie SSD/NVMe presunutím /var/log na ramdisk tmpfs, ktorý sa podľa plánu zapisuje na disk. Inštaluje sa z pôvodného projektu, keď je systémový disk SSD alebo NVMe. Veľkosť ramdisku určí podľa RAM hostiteľa (128M / 256M / 512M) a pridá kontrolu, ktorá zrotuje a zapíše logy, keď ramdisk prekročí 85 %. Journal a logy úloh Proxmox VE a Proxmox Backup Server zostávajú na disku. Všetky podrobnosti v Log2RAM.",
"category": "Úložisko",
"categorySlug": "storage"
},
@@ -109,7 +109,7 @@
}
],
"hardwareTitle": "Predvolené hodnoty podľa hardvéru",
- "hardwareBody": "Skript pred nastavením preskúma hardvér hostiteľa. Log2RAM sa predvolene inštaluje iba pri koreňovom disku SSD alebo NVMe; pri inej detekcii žiada potvrdenie. Veľkosť ramdisku sa vyberá podľa celkovej RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). Pri aktívnom Log2RAM sa limity journald prispôsobia ramdisku, aby sa nezaplnil.",
+ "hardwareBody": "Skript pred nastavením preskúma hardvér hostiteľa. Log2RAM sa predvolene inštaluje iba pri koreňovom disku SSD alebo NVMe; pri inej detekcii žiada potvrdenie. Veľkosť ramdisku sa vyberá podľa celkovej RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). Keď je Log2RAM aktívny, journal zostáva na disku s vlastným limitom.",
"upgrade": {
"heading": "Aktualizovať systém",
"intro": "Prvým krokom automatického postupu je aktualizácia Proxmoxu. Oficiálny návod pre bežiaci hostiteľ v rámci rovnakej hlavnej verzie odporúča spustiť:",
diff --git a/web/messages/sk/docs/post-install/optional.json b/web/messages/sk/docs/post-install/optional.json
index ee101952..ebfa24fc 100644
--- a/web/messages/sk/docs/post-install/optional.json
+++ b/web/messages/sk/docs/post-install/optional.json
@@ -133,25 +133,25 @@
},
"log2ram": {
"title": "Nainštalovať Log2RAM (zníženie opotrebenia SSD/NVMe)",
- "intro": "Log2RAM pripojí /var/log ako ramdisk tmpfs a pravidelne ho synchronizuje na disk. Ukladanie častých zápisov journald do RAM môže znížiť opotrebenie SSD a diskové I/O. Synchronizácia a riadne vypnutie uložia logy na disk, ale náhly výpadok napájania môže stratiť záznamy od poslednej synchronizácie.",
+ "intro": "Log2RAM pripojí /var/log na ramdisk tmpfs a podľa plánu ho zapisuje na disk, takže logy, ktoré sa neustále menia, napríklad prístupový log pveproxy, sa zapisujú do RAM namiesto na systémový SSD. Journal a logy úloh Proxmox VE a Proxmox Backup Server zostávajú na disku. Čisté vypnutie zapíše ramdisk na disk; pri náhlom výpadku napájania sa stratia len textové logy zapísané od poslednej synchronizácie.",
"upstreamLabel": "Pôvodný projekt:",
"upstreamUrl": "https://github.com/azlux/log2ram",
"upstreamLinkLabel": "azlux/log2ram na GitHub",
"doesLabel": "Čo robí ProxMenux:",
"doesItems": [
"Čítaním /sys/block/<dev>/queue/rotational zistí, či je koreňový disk SSD/NVMe. Pri rotačnom disku sa automatický postup pred inštaláciou opýta.",
- "Naklonuje pôvodný repozitár (azlux/log2ram) do /tmp/log2ram a upraví install.sh: nahradí rsync -aAXv príkazom rsync -aXv --no-acls. Predíde tak chybe set_acl: Operation not supported s návratovým kódom 23 pri presune /var/log na súborovom systéme bez ACL. Jednotka systemd log2ram sa po inštalácii zapne.",
+ "Nainštaluje Log2RAM 1.7.2 z pôvodného projektu, overí, že stiahnutý súbor je táto verzia, a jeho kód ponechá bez zmien. Chybový e-mail vypne pomocou NOTIFICATION=false, pretože Proxmox VE neobsahuje príkaz mail.",
"Nastaví veľkosť ramdisku podľa RAM hostiteľa: ≤ 8 GB → 128M, ≤ 16 GB → 256M, > 16 GB → 512M. Zapíše hodnotu do SIZE= v /etc/log2ram.conf.",
- "Plánuje periodickú synchronizáciu disku cez /etc/cron.d/log2ram: každý 1h / 3h / 6h podľa rovnakej úrovne RAM.",
- "Nainštaluje /usr/local/bin/log2ram-check.sh, spúšťaný cez /etc/cron.d/log2ram-auto-sync každých 10 minút. Keď /var/log dosiahne 80% ramdisku, vyčistí staré žurnály. Pri 92% najprv spustí logrotate -f /etc/logrotate.d/proxmox-backup-api, ak súbor existuje: logy API PBS bývajú najväčším spotrebiteľom miesta na hostiteľovi PBS. Potom skráti pveproxy access/error a pveam.log pred synchronizáciou. Samotný log2ram write skopíruje tmpfs na disk, ale nezmenší ho. Ochrana pomáha predchádzať pádom PVE s No space left on device pri nekontrolovanom raste logov.",
- "Zistí službu proxmox-backup-server na hostiteľovi. Ak existuje, vytvorí /etc/logrotate.d/proxmox-backup-api (20 MB × 3 rotácie) a /etc/cron.hourly/proxmox-backup-logrotate, aby obmedzil logy API PBS, častý zdroj rastu, aj medzi synchronizáciami Log2RAM. Bez PBS nič nezmení.",
- "Prispôsobí limity systemd-journald (SystemMaxUse, RuntimeMaxUse) ramdisku, aby ho nárazový príval záznamov nezaplnil.",
- "Zaznamená sa v installed_tools.json, aby sa dal vrátiť cez Odinštalovať optimalizácie."
+ "Ponechá na disku journal (/var/log/journal), logy úloh Proxmox VE (/var/log/pve) a, ak je nainštalovaný, logy Proxmox Backup Server (/var/log/proxmox-backup), v /var/hdd.journal, /var/hdd.pve a /var/hdd.proxmox-backup. Každá bežná cesta sa zmení na odkaz na svoj priečinok na disku. Presun prebehne pri štarte, ešte pred spustením Log2RAM.",
+ "Zapisuje ramdisk na disk časovačom Log2RAM každú 1 h / 3 h / 6 h podľa rovnakého pásma RAM a po každom zápise vyprázdni vyrovnávaciu pamäť disku, takže dokončená synchronizácia prežije výpadok napájania.",
+ "Nainštaluje kontrolu v /etc/cron.d/log2ram-auto-sync, ktorá sa spúšťa každých 10 minút. Keď /var/log prekročí 85 % ramdisku, zrotuje logy, skomprimuje práve zrotované a všetko zapíše na disk. Žiadny log sa nevyprázdňuje.",
+ "Ponechá journal systemd na disku s limitom 1 GB, v /etc/systemd/journald.conf.d/proxmenux-log2ram.conf. Na hostiteľovi s Proxmox Backup Server pridá /etc/logrotate.d/proxmox-backup-api (rotácia 20 MB × 3) pre jeho logy API.",
+ "Zaregistruje sa v installed_tools.json, aby ho bolo možné vrátiť v časti Odinštalovať optimalizácie. Aktualizácia zo staršej verzie zachová veľkosť a interval synchronizácie a pri ďalšom štarte presunie journal na disk."
],
"howUseLabel": "Ako používať:",
- "howUseBody": "Automatický postup nainštaluje Log2RAM bez zásahu pri zistení koreňového SSD/NVMe. Prispôsobiteľný postup sa pýta na veľkosť, interval a ochranu synchronizácie. Výzva uvádza 90%, no vytvorený skript používa vyššie opísané limity 80% na čistenie a 92% na intenzívne čistenie.",
+ "howUseBody": "V automatickom postupe sa Log2RAM nainštaluje bez zásahu, keď zistí systémový disk SSD/NVMe. Prispôsobiteľný postup sa pýta na veľkosť, interval synchronizácie a na to, či zapnúť kontrolu. Zmeny sa uplatnia pri ďalšom štarte.",
"verifyLabel": "Overiť a spravovať zo shellu:",
- "verifyCode": "# Service state and configured timers\nsystemctl status log2ram\nsystemctl list-timers log2ram*\n\n# Current ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Force a sync now (tmpfs → disk); does NOT shrink the tmpfs\nlog2ram write\n\n# Trigger the auto-sync guard on demand (vacuum + sync when usage is high)\n/usr/local/bin/log2ram-check.sh\n\n# Current config: SIZE, mail settings, path\ncat /etc/log2ram.conf\n\n# ProxMenux-specific cron jobs\ncat /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
+ "verifyCode": "# Service state and sync timer\nsystemctl status log2ram\nsystemctl list-timers log2ram-daily.timer\n\n# Ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Folders kept on disk, and the journal size\nls -l /var/log | grep -- '->'\njournalctl --disk-usage\n\n# Write the ramdisk to disk now\nsystemctl reload log2ram\n\n# Current configuration\ncat /etc/log2ram.conf\nsystemctl cat log2ram\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
},
"zram": {
"title": "Povoliť výmenný profil ZRAM",
diff --git a/web/messages/sk/docs/post-install/uninstall.json b/web/messages/sk/docs/post-install/uninstall.json
index ee7856ba..69751827 100644
--- a/web/messages/sk/docs/post-install/uninstall.json
+++ b/web/messages/sk/docs/post-install/uninstall.json
@@ -125,7 +125,7 @@
},
{
"tool": "Log2RAM",
- "restores": "Zastaví a zakáže službu a časovač. Čistí úlohy cron, jednotky systemd, binárne súbory, konfiguračné súbory a adresár /var/log.hdd. Tiež odinštaluje balík apt, ak bol nainštalovaný týmto spôsobom."
+ "restores": "Vypne službu a časovač a odstráni konfiguráciu ProxMenux pre Log2RAM. Pri ďalšom vypnutí Log2RAM zapíše /var/log na disk; pri ďalšom štarte sa journal a logy úloh vrátia do /var/log a súbory Log2RAM sa odstránia."
},
{
"tool": "Automatický TRIM ZFS",
diff --git a/web/messages/sv/docs/post-install/automated.json b/web/messages/sv/docs/post-install/automated.json
index c3fc9aa6..1523cec6 100644
--- a/web/messages/sv/docs/post-install/automated.json
+++ b/web/messages/sv/docs/post-install/automated.json
@@ -79,7 +79,7 @@
},
{
"tool": "Log2RAM (SSD-medveten, auto)",
- "what": "Minskar SSD-/NVMe-slitage genom att flytta /var/log till en tmpfs-ramdisk med regelbunden synkronisering till disk. Installation från ursprungsprojektet när rotdisken är SSD eller NVMe. Storlek efter värdens RAM (128M / 256M / 512M), periodisk synkronisering och skydd som rensar äldre journaler vid 80% fyllnadsgrad och trunkerar snabbt växande loggar vid 92% före skrivning. Anpassar journald-gränser till ramdisken. Detaljer på Log2RAM.",
+ "what": "Minskar slitaget på SSD/NVMe genom att flytta /var/log till en tmpfs-ramdisk som skrivs till disken enligt ett schema. Installeras från uppströmsprojektet när systemdisken är SSD eller NVMe. Ramdiskens storlek väljs efter värdens RAM (128M / 256M / 512M) och en bevakning roterar och skriver loggarna när ramdisken passerar 85 %. Journalen och uppgiftsloggarna för Proxmox VE och Proxmox Backup Server ligger kvar på disken. Alla detaljer under Log2RAM.",
"category": "Lagring",
"categorySlug": "storage"
},
@@ -109,7 +109,7 @@
}
],
"hardwareTitle": "Hårdvaruanpassade standardvärden",
- "hardwareBody": "Skriptet läser värdhårdvaran innan inställningar tillämpas. Log2RAM är bara installerat på SSD / NVMe rotdiskar som standard (den frågar om upptäckten säger något annat). Log2RAM ramdiskstorlek väljs från totalt RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). De journald gränser matchas till ramdiskstorleken när Log2RAM är aktivt, för att undvika att fylla det.",
+ "hardwareBody": "Skriptet läser värdhårdvaran innan inställningar tillämpas. Log2RAM är bara installerat på SSD / NVMe rotdiskar som standard (den frågar om upptäckten säger något annat). Log2RAM ramdiskstorlek väljs från totalt RAM (≤ 8 GB → 128M, ≤ 16 GB → 256M, '>' 16 GB → 512M). När Log2RAM är aktivt ligger journalen kvar på disken med en egen gräns.",
"upgrade": {
"heading": "Uppdatera och uppgradera systemet",
"intro": "Det första steget i Automated-paketet är Proxmox-uppgraderingen. Proxmox egen uppgraderingsvägledning för en löpande värd (inom samma stora version) är att köra:",
diff --git a/web/messages/sv/docs/post-install/optional.json b/web/messages/sv/docs/post-install/optional.json
index e1811788..e96d83ac 100644
--- a/web/messages/sv/docs/post-install/optional.json
+++ b/web/messages/sv/docs/post-install/optional.json
@@ -133,25 +133,25 @@
},
"log2ram": {
"title": "Installera Log2RAM (SSD/NVMe slitage reduktion)",
- "intro": "Log2RAM monterar /var/log som en tmpfs-ramdisk och synkroniserar regelbundet till disk. Att buffra journalds täta skrivningar i RAM kan minska SSD-slitage och disk-I/O. Synkronisering och normal avstängning sparar loggar, men ett plötsligt strömavbrott kan förlora poster sedan senaste synkroniseringen.",
+ "intro": "Log2RAM monterar /var/log på en tmpfs-ramdisk och skriver den till disken enligt ett schema, så att loggar som ändras hela tiden, som pveproxys åtkomstlogg, skrivs till RAM i stället för till systemets SSD. Journalen och uppgiftsloggarna för Proxmox VE och Proxmox Backup Server ligger kvar på disken. En ren avstängning skriver ramdisken till disken; ett plötsligt strömavbrott förlorar bara de textloggar som skrivits sedan den senaste synkroniseringen.",
"upstreamLabel": "Upstream projekt:",
"upstreamUrl": "https://github.com/azlux/log2ram",
"upstreamLinkLabel": "azlux/log2ram på GitHub",
"doesLabel": "Vad ProxMenux gör:",
"doesItems": [
"Detekterar om rotskivan är SSD/NVMe genom att läsa /sys/block/<dev>/queue/rotational. På en rotationsdisk frågar det automatiska flödet först innan du installerar.",
- "Kloner uppströms repository (azlux/log2ram) i /tmp/log2ram och patchar sin install.sh, ersätta rsync -aAXv med rsync -aXv --no-acls. Denna justering undviker set_acl: Operation not supported exit 23 misslyckande att vaniljskriptet utlöser när man flyttar /var/log på filsystem utan ACL stöd. log2ram systemd-enheten är aktiverad efter installation.",
+ "Installerar Log2RAM 1.7.2 från uppströmsprojektet, kontrollerar att nedladdningen är den versionen och lämnar koden oförändrad. Felmejlet stängs av med NOTIFICATION=false, eftersom Proxmox VE inte har något mail-kommando.",
"Anpassar ramdiskens storlek efter värdens RAM: ≤ 8 GB → 128M, ≤ 16 GB → 256M, > 16 GB → 512M. Skriver värdet till SIZE= i /etc/log2ram.conf.",
- "Schemalägger regelbunden disksynkronisering via /etc/cron.d/log2ram: var 1 / 3 / 6 timme enligt samma RAM-nivå.",
- "Installerar /usr/local/bin/log2ram-check.sh, som körs var tionde minut via /etc/cron.d/log2ram-auto-sync. När /var/log når 80% av ramdisken rensas äldre journaler. Vid 92% körs först logrotate -f /etc/logrotate.d/proxmox-backup-api om filen finns: PBS API-loggar tar ofta mest utrymme på en PBS-värd. Sedan trunkeras pveproxy access/error och pveam.log före synkronisering. log2ram write kopierar bara tmpfs till disk utan att krympa det. Skyddet hjälper till att förebygga PVE-fel med No space left on device när loggar växer okontrollerat.",
- "Upptäcker tjänsten proxmox-backup-server på värden. Om den finns skapas /etc/logrotate.d/proxmox-backup-api (20 MB × 3 rotationer) och /etc/cron.hourly/proxmox-backup-logrotate för att begränsa PBS API-loggar, den vanliga tillväxtkällan, även mellan Log2RAM-synkroniseringar. Utan PBS görs ingen ändring.",
- "Justerar systemd-journald gränser (SystemMaxUse, RuntimeMaxUse) för att passa in i ramdisken så att en enda burst inte kan fylla den.",
- "Registreras i installed_tools.json så att det kan återställas via Avinstallera optimeringar."
+ "Behåller journalen (/var/log/journal), uppgiftsloggarna för Proxmox VE (/var/log/pve) och, där det är installerat, loggarna för Proxmox Backup Server (/var/log/proxmox-backup) på disken, i /var/hdd.journal, /var/hdd.pve och /var/hdd.proxmox-backup. Varje vanlig sökväg blir en länk till sin mapp på disken. Flytten sker vid uppstart, innan Log2RAM startar.",
+ "Skriver ramdisken till disken med Log2RAMs timer var 1:e / 3:e / 6:e timme enligt samma RAM-nivå och tömmer diskens cache efter varje skrivning, så att en avslutad synkronisering klarar ett strömavbrott.",
+ "Installerar en bevakning i /etc/cron.d/log2ram-auto-sync som körs var 10:e minut. När /var/log passerar 85 % av ramdisken roterar den loggarna, komprimerar de nyss roterade och skriver allt till disken. Ingen logg töms.",
+ "Behåller systemd-journalen på disken med en gräns på 1 GB, i /etc/systemd/journald.conf.d/proxmenux-log2ram.conf. På en värd med Proxmox Backup Server läggs /etc/logrotate.d/proxmox-backup-api (rotation 20 MB × 3) till för dess API-loggar.",
+ "Registrerar sig i installed_tools.json så att den kan återställas från Avinstallera optimeringar. En uppdatering från en tidigare version behåller storleken och synkroniseringsintervallet och flyttar journalen till disken vid nästa uppstart."
],
"howUseLabel": "Hur man använder:",
- "howUseBody": "Det automatiserade flödet installerar Log2RAM utan åtgärd när en SSD-/NVMe-rotdisk upptäcks. Det anpassningsbara flödet frågar om storlek, intervall och synkskydd. Frågan anger 90%, men det genererade skriptet använder trösklarna 80% för rensning och 92% för intensiv rensning som beskrivs ovan.",
+ "howUseBody": "I det automatiserade flödet installeras Log2RAM utan ingrepp när en SSD/NVMe-systemdisk upptäcks. Det anpassningsbara flödet frågar efter storlek, synkroniseringsintervall och om bevakningen ska aktiveras. Ändringarna träder i kraft vid nästa uppstart.",
"verifyLabel": "Verifiera och hantera från skalet:",
- "verifyCode": "# Service state and configured timers\nsystemctl status log2ram\nsystemctl list-timers log2ram*\n\n# Current ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Force a sync now (tmpfs → disk); does NOT shrink the tmpfs\nlog2ram write\n\n# Trigger the auto-sync guard on demand (vacuum + sync when usage is high)\n/usr/local/bin/log2ram-check.sh\n\n# Current config: SIZE, mail settings, path\ncat /etc/log2ram.conf\n\n# ProxMenux-specific cron jobs\ncat /etc/cron.d/log2ram /etc/cron.d/log2ram-auto-sync\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
+ "verifyCode": "# Service state and sync timer\nsystemctl status log2ram\nsystemctl list-timers log2ram-daily.timer\n\n# Ramdisk usage — /var/log IS the tmpfs itself\ndf -h /var/log\n\n# Folders kept on disk, and the journal size\nls -l /var/log | grep -- '->'\njournalctl --disk-usage\n\n# Write the ramdisk to disk now\nsystemctl reload log2ram\n\n# Current configuration\ncat /etc/log2ram.conf\nsystemctl cat log2ram\n\n# Recent Log2RAM activity\njournalctl -u log2ram -n 50 --no-pager"
},
"zram": {
"title": "Aktivera ZRAM-swap-profil",
diff --git a/web/messages/sv/docs/post-install/uninstall.json b/web/messages/sv/docs/post-install/uninstall.json
index c8fad698..b4fce73a 100644
--- a/web/messages/sv/docs/post-install/uninstall.json
+++ b/web/messages/sv/docs/post-install/uninstall.json
@@ -125,7 +125,7 @@
},
{
"tool": "Log2RAM",
- "restores": "Stoppar och inaktiverar tjänst och timer. Tar bort cron-jobb, systemd-enheter, binärer, konfiguration och /var/log.hdd. Avinstallerar även apt-paketet om det installerats så."
+ "restores": "Inaktiverar tjänsten och timern och tar bort ProxMenux-konfigurationen för Log2RAM. Vid nästa avstängning skriver Log2RAM /var/log till disken; vid nästa uppstart flyttas journalen och uppgiftsloggarna tillbaka till /var/log och Log2RAM-filerna tas bort."
},
{
"tool": "Automatisk ZFS-TRIM",