ProxMenux OCI laboratory
Prototype that converts official image documentation and discovered Docker Compose definitions into one ProxMenux JSON template per distribution or deployment profile, then optionally installs reviewed images as native Proxmox VE OCI LXC containers.
This repository is a laboratory. Generated templates are not considered compatible merely because conversion succeeded.
Design
catalog/index.jsonis the lightweight application listing.catalog/apps/<app>.jsonis the canonical template for one image.schemas/oci-template.schema.jsonvalidates generated templates.container_contractpreserves the official Compose contract and source text.catalog_uicontains neutral store metadata and attributes each image to its actual image repository or publisher.first_runrecords detected web endpoints, documented default credentials, and supported methods for recovering credentials generated at runtime.proxmoxcontains only native OCI/LXC translation and documented adaptations.- A multi-image application remains one catalog entry and one user-facing
installation. Its
compose_stackcreates one native OCI LXC per service, allocates a private network automatically and orchestrates dependencies. - Discovery catalogs are never recorded as image authors or repositories in public templates.
- Imported store text keeps only
en_USandes_ES; when Spanish is missing,es_ESfalls back to English instead of retaining unused source locales. - Distributions and deployment profiles remain separate entries. For example,
nextcloudis the LinuxServer image,nextcloud-officialis the official single-image deployment andnextcloud-stackis the laboratory-derived Nextcloud, PostgreSQL and Redis profile. The namenextcloud-aiois reserved for the distinct upstream All-in-One project. - Hardware choices share one application image: LinuxServer
jellyfinoffers CPU, VA-API, AMD/OpenCL, Intel/OpenCL and NVIDIA in its installer profile. OpenCL choices use official LinuxServer mods, not separate image variants. See Jellyfin GPU laboratory for tested capabilities, native OCI device permissions and the distinction between VA-API, OpenCL and Vulkan tone mapping. - Curated laboratory profiles can replace a duplicate discovery identifier.
jdownloaderuses the maintained JDownloader 2 image from jlesage and replaces the less clear discovered namejdownloader2; these are not two different generations of JDownloader. - The curated
frigateprofile replaces the generic discovered deployment and preserves the AMD VA-API and OpenVINO CPU choices. The installer keeps the official rollingstableimage intact; package substitutions require a separate validation on stable hardware and are not enabled automatically. - The curated
paperless-ngxprofile translates the official PostgreSQL Compose into three native LXCs: Paperless-ngx, PostgreSQL and Valkey. Private state uses backed-up Proxmox volumes, whileconsumeandexportcan use either managed volumes or host directories shared with scanners and other applications. - The curated
rcloneprofile uses a two-phase workflow. Installation starts the official authenticated WebUI so the user can create and authorize a private remote. The separaterclone-mountaction then validates that remote, enables the official FUSE mount and publishes distinct read/write and recursively read-only paths for other native LXCs.
Catalog maintenance
The catalog is generated rather than written by hand. A contributor adding an application runs the orchestrator directly from this directory:
export PYTHONPATH=src
python3 -m proxmenux_oci sync
python3 -m proxmenux_oci list --filter sonarr
python3 -m proxmenux_oci generate sonarr
python3 -m proxmenux_oci show sonarr
GITHUB_TOKEN=github_pat_xxx python3 -m proxmenux_oci generate-all
generate writes one application's template from its published recipe; show
prints what the installation would create, which is the fastest way to see
whether a translation came out right before installing anything.
It needs python3-yaml and python3-jsonschema. On Debian and Proxmox both
are distribution packages; install them with APT before generating the catalog.
GITHUB_TOKEN is optional and only raises the public API rate limit, which the
full generate-all pass reaches. Never commit a token; .env files are
ignored.
Safety boundary
Automatic installation is allowed only when every Compose behavior has a
reviewed and tested native Proxmox translation. Multi-image applications are
modeled as one installation; generic stacks remain blocked until their native
multi-LXC orchestrator is implemented and validated, while curated Immich,
Nextcloud and Paperless-ngx stacks have dedicated orchestrators. Privileged
mode and relaxed AppArmor/seccomp profiles require an explicit high-risk
confirmation. Security requirements are classified by capability instead of
assuming that every Compose privileged: true is an image requirement:
requires_privileged_lxcis reserved for a reviewed profile whose native LXC adaptation has proved that broad privilege is necessary.optional_privileged_lxcrecords an upstream compatibility request. The installer keeps the LXC unprivileged by default and offers the broader mode only after explicit confirmation.- GPU, USB and serial hardware are passed as individual Proxmox devices and do not imply a privileged LXC.
- Required Compose AppArmor/seccomp relaxations need explicit confirmation.
Relaxations marked
#optionalremain disabled by default and are offered as individual compatibility choices instead of being labeled as mandatory. pid: hostis tracked separately as host PID namespace access. It remains blocked until a safe native LXC translation is validated; it must never be mislabeled as ordinary GPU access or silently promoted to privileged mode.
Before creating an LXC, the tool prints a redacted deployment plan and requires
the exact confirmation INSTALAR. Remote credentials are handled by normal SSH;
they are never placed in a template or command argument.
Image downloads run in a pseudo-terminal when script is available, allowing
Skopeo layer progress to remain visible. The final result lists the detected IP,
one complete URL per documented web endpoint, and any public default login from
the upstream LinuxServer Application Setup section. Non-web service ports are
not presented as browser URLs. Public default passwords should be changed after
the first login.
Some images do not publish a static password. For example, qBittorrent prints a
temporary password for admin during startup. When this behavior is explicitly
documented upstream, the installer enables a short-lived native LXC console log
for the first boot, extracts the password, removes the log and its temporary
configuration, and prints the credential in the terminal. The generated secret
is never written back to the reusable catalog JSON or to Proxmox metadata.
When run as root directly on a Proxmox node, destination auto selects local
execution and does not open an SSH connection back to the same node. Outside
Proxmox, specify root@<node-address> using --host or the interactive prompt;
the installer never assumes a laboratory address.
Native Proxmox behavior
Proxmox VE 9 imports OCI Entrypoint, Cmd, Env, User, WorkingDir and
StopSignal when pct create extracts the OCI archive. The installer preserves
that behavior and only overlays values explicitly present in Compose.
Proxmox VE 9.2 cannot extract the tested OCI archive directly as a privileged LXC. When a reviewed profile explicitly requires privileges, the installer imports it with the standard unprivileged idmap, converts ownership before the first start while preserving extended attributes, and only then switches the native LXC configuration to privileged mode. This conversion is never applied without the user's high-risk confirmation.
Compose stop_grace_period is recorded as the timeout for ProxMenux-managed
pct shutdown operations. It is not mapped to Proxmox startup.down, because
that field controls sequencing between guests rather than the CT stop timeout.
Persistent paths can be installed as:
- Proxmox-managed
mpNvolumes with the image's original container path andbackup=1by default. - Existing host bind mounts for data intentionally shared with other LXCs.
- Omitted mounts only when LinuxServer marks them optional.
When a host bind is selected, the installer proposes
/mnt/oci-shared/<application>/<volume>. Missing data directories are created
automatically with ownership mapped for the unprivileged LXC; existing
directories are never re-owned. System files and runtime sockets are excluded
from automatic creation.
Hardware devices keep their host path and obtain their numeric GID directly
from the selected host device; ProxMenux does not assume fixed video or
render group IDs. NVIDIA profiles additionally require a working host driver
and NVIDIA Container Toolkit. At installation time, nvidia-container-cli
supplies the current device, binary, firmware and driver-library inventory;
ProxMenux translates it to native Proxmox devN entries and read-only LXC file
mounts, including the compatibility links expected by the image. No driver
version or library list is hardcoded in the template.
Compose network_mode: host means the network namespace of the dedicated LXC,
not the Proxmox host network. bridge and default use the same native LXC
model because no Docker NAT layer exists. Recognized cap_add values are
checked against the LXC capability model instead of using lxc.cap.keep, which
would accidentally remove other capabilities. WireGuard images that request
SYS_MODULE preload and verify the wireguard kernel module on the Proxmox
host while keeping the LXC unprivileged.
Namespaced IPv4/IPv6 Compose sysctls are written to an LXC include profile in
/etc/pve/lxc/<VMID>.proxmenux-sysctls; this is required because Proxmox 9.2
does not accept arbitrary network sysctl keys directly in the managed CT
configuration. The profile is stored on the Proxmox cluster filesystem and is
removed on a failed installation. Cross-host backup/restore validation remains
pending, like the versioned NVIDIA host-driver mounts.
seccomp:unconfined uses a valid empty LXC denylist profile, never /dev/null.
apparmor:unconfined is applied with its native LXC directive, and
no-new-privileges maps to lxc.no_new_privs. Docker's label:disable and
logging drivers remain source metadata because the native LXC runtime has no
Docker SELinux label or Docker log object. Compose supplementary groups are
resolved from the image's /etc/group and applied through lxc.init.groups.
The current laboratory implementation records versioned NVIDIA driver paths in the LXC configuration. After updating the NVIDIA host driver, those mounts must be regenerated before affected LXCs are started. Automatic profile refresh is part of the future update lifecycle and is not yet implemented.
For multi-image applications, users choose only normal deployment values such as storage destinations, frontend network and shared data paths. ProxMenux must reserve all VMIDs atomically, create the dependency network, assign internal addresses and service aliases, generate shared secrets, create every LXC and start dependencies in health-checked order. Private configuration and database paths use Proxmox-managed volumes with backup enabled; only intentionally shared user data uses host bind mounts.
Validated multi-LXC installers attach an official Proxmox hookscript to the
main LXC. ProxMenux only creates this lifecycle configuration during
installation; no ProxMenux daemon remains running. On every later pre-start,
Proxmox starts any stopped dependency in declared order and waits for its
healthcheck before allowing the main LXC to start. Stopping the main LXC does
not implicitly stop its dependencies, so a restart cannot interrupt a database
or cache before the application has shut down.
Host bind paths selected by the user are created when missing and need an
independent backup policy; no shared path is created unless host-bind was
chosen. The default LXC is unprivileged and uses an 8 GB thin-provisioned
rootfs.
Validation lifecycle
Generated templates start as generated-unvalidated. Promotion requires:
- Clean installation.
- Application health check.
- Restart persistence.
- Proxmox backup and restore.
- Image replacement with persistent volumes preserved.
- Review of every platform adaptation and unsupported feature.
Real tests are recorded in catalog/verification.json, which the catalog
reads when it loads and applies on top of the generated templates and the
overlays, so a new entry shows without regenerating and a regeneration keeps
it: "status": "laboratory-validated" for an application tested in the
ProxMenux lab, or "community_tested": {"by": "<GitHub user>", "date": "<YYYY-MM-DD>", "report": "<link to the report>"} for one tested by the
community. Both show as verified in the OCI installer.
VALIDATION.md lists the verified applications and explains
how to take part. It is generated from catalog/verification.json with
python3 .github/scripts/oci_validation.py render, and CI checks that it is
current.
The mini changelog comes from the LinuxServer README Versions section. At
installation, the architecture-specific registry digest and image labels are
recorded back into the local app JSON for future update comparisons.
Generic multi-LXC installer
The generic stack compiler now handles an application with official PostgreSQL
and Redis/Valkey dependencies. It reuses install_oci.sh for image verification
and native OCI import. Docmost and Blinko are the first eligible catalog entries;
real installation and restart validation of this new driver remain pending.
Each other stack records its unresolved semantics in proxmox.generic_stack_review.
The compiler resolves generated secrets once per stack and binds PostgreSQL URL
credentials to the named database service. The installer allocates a private
network, writes service aliases, creates data volumes with the image's initial
files and ownership, and registers the Proxmox dependency hook before starting
the main LXC. It replaces example localhost public URLs with the assigned LAN IP
after a successful first boot and performs a graceful restart to apply them.
Using a stable DHCP lease or a domain is necessary if that address later changes.
For the rolling official PostgreSQL image, legacy /var/lib/postgresql/data
mounts become /var/lib/postgresql, preserving its versioned data directory.
Allocation is serialized among ProxMenux installers on the node; native pct
creation checks still arbitrate collisions with concurrent external operations.
A failed generic stack keeps completed LXCs and their volumes for diagnosis.
After a partial failure, inspect these resources before starting a new install.
No claim of atomic cross-cluster allocation or coordinated backup is made.
The hook snippet and /etc/pve/priv/proxmenux-stack-<vmid>.json are host artifacts;
back them up separately and remap VMIDs/recreate the bridge when restoring a
whole stack on another host. A normal LXC backup alone does not package these
host artifacts. This restoration workflow is still pending validation.
Suite Arr (selectable media stack)
The suite-arr catalog entry offers Prowlarr, Sonarr, Radarr, qBittorrent,
Lidarr, Bazarr, SABnzbd, Seerr and Unpackerr. The first four are checked by
default. A separate single-choice menu offers Jellyfin (default), Plex, Emby
or no media server. Only selected services are created.
It reuses individual image templates, including the official Seerr and Unpackerr
images. Each /config (Seerr: /app/config) is a separate managed Proxmox volume
with backup enabled. Media applications share a user-selected host directory at
/data; media is not included
in container backups. New media directories receive mapped UID/GID 1000 ownership;
existing directories and their permissions are not recursively changed.
Web applications have LAN access and a private address for inter-service APIs.
Unpackerr has only a private address and no WebUI. Its initial start is deferred
until the selected Arr apps have generated API keys, then official UN_*
environment variables are persisted in its LXC config. Subsequent starts are independent and controlled by Proxmox onboot on each LXC.
Save the LXC configuration alongside application-volume backups.
The installer reads generated API keys, creates media root folders, and connects
selected Sonarr/Radarr instances to Prowlarr using its API schema. It does not
patch application binaries, add indexers, download content, disable authentication,
or install a VPN. First-login Arr authentication, indexers and quality profiles
still require user configuration. If qBittorrent is not selected, the download
client also needs manual configuration.
The shared tree is downloads/{tv,movies,music,incomplete,usenet,usenet-incomplete}
and media/{movies,series,music}. Subtitles reside beside their media files.
SABnzbd's new persistent config sets both incomplete and complete download paths
under /data; no download content defaults to the container rootfs.
Jellyfin retains its 4 cores, 4096 MB RAM, 16 GB config volume and hardware selector.
Other media servers retain their image's hardware options. Accounts/library setup,
Seerr and Bazarr connections, Lidarr profiles/root folder/download client, SABnzbd
Usenet credentials/client connections and subtitle providers still require user
configuration. These integrations are not claimed as automatic. Gluetun/VPN is
explicitly deferred, not installed or advertised as protecting traffic.
Seerr preserves the upstream non-root image user. Its documented security settings
map to lxc.no_new_privs: 1 and lxc.cap.keep: none after clearing inherited drop
entries. All-capability removal is incompatible with requested additional capabilities.
With qBittorrent selected, the user chooses the password for admin. The installer
copies the image's own default configuration into its new /config volume and
sets the upstream PBKDF2-SHA512 password hash; existing config is never overwritten.
No image files are patched. The API configures /data/downloads/, its incomplete
subdirectory, and selected tv/movies categories. Sonarr/Radarr download clients
are tested before saving. All apps use the same paths, with no remote path mappings.
Login accepts the legacy HTTP 200/Ok./SID response or the qBittorrent 5.2
HTTP 204/empty-body/QBT_SID_<port> response. A protected preferences request must
also succeed before any settings are changed. The password is masked in deployment
previews and returned in the final terminal credentials; protect terminal output.
It is stored hashed in qBittorrent and by the Arr apps in their private databases.
Suite Arr has no primary container, dependency hook or lifecycle contract. The installer starts each selected application once to perform initial setup; that one-time sequence does not couple future starts or stops. The user's onboot choice is applied to each LXC individually. True dependent stacks (Immich, Nextcloud and generic multi-image stacks) retain their existing hook lifecycle. Failure preserves created containers/data. The suite remains unvalidated by a complete real installation; tests cover compilation, independent startup and APIs.
Common dependency profiles
The generic stack compiler also supports official mariadb, mongo and
getmeili/meilisearch dependencies. It uses MariaDB's bundled
healthcheck.sh --connect --innodb_initialized, MongoDB's mongosh ping, and
Meilisearch's /health endpoint. Missing implicit data volumes are materialized
as backed-up Proxmox volumes at their official paths; dependencies stay on the
private network with no LAN interface. The Proxmox host can still reach them.
Per-stack stack_environment_overrides and stack_generators in catalog overlays
correct imported metadata without editing upstream images. Monica gets a 32-byte
base64 application key and a boolean random-root-password switch. Linkwarden gets
boolean credential login and a storage path matching its persisted volume.
Petio's setup should use MongoDB host mongo, port 27017; Plex credentials remain
user-provided. No third-party API credentials are fabricated.
These profiles enable monica-official, petio and linkwarden as installable,
not runtime-validated. Latest image tags remain selected; compatibility between
current upstream releases, initial application setup and restart/restore need
real laboratory testing. In particular, current MongoDB images require compatible
CPU instructions. Profiles do not grant LAN access to database services, enable
unattended upgrades or promise that a new database major version can reuse an old
data directory without migration.
RomM and optional external credentials
RomM now uses the common MariaDB profile and binds DB_PASSWD to the same
installation-generated value as MARIADB_PASSWORD. Root credentials remain
independent. The overlay declares optional IGDB, ScreenScraper and SteamGridDB
credential groups via stack_optional_environment. They are requested only if
selected; skipped providers have no fabricated credentials. Entered secrets are
preserved literally (including dollar signs) and hidden in deployment summaries.
All five RomM data/configuration volumes default to backed-up private storage;
users may explicitly select shared host directories.
RomM remains installable without runtime validation. The hook starts dependencies before the main application but does not implement Compose's propagation of an explicit dependency restart to RomM. Upstream latest versions, first-run setup and restart/restore behavior still need real installation testing.
Teable and authenticated Redis
Teable uses three OCI LXC services: the app, PostgreSQL and Redis. The overlay
replaces the malformed imported Redis argument with a reviewed three-argument
redis-server --requirepass command, preserving the image entrypoint. The Redis
server password, REDISCLI_AUTH and Teable's Redis URI share one generated value.
Only this explicit authenticated Redis command profile is accepted; arbitrary
custom dependency commands remain blocked.
Redis healthchecks now require an exact PONG response. The hook does not embed
the password: the authenticated check obtains REDISCLI_AUTH inside the LXC.
Credentials remain readable to administrators in native runtime configuration.
A Compose internal network's name is treated as a label, not a fixed Proxmox
bridge name; external networks and custom IPAM remain unsupported by this driver.
Teable is installable but not yet runtime-validated; tests include a fake Redis CLI
that returns authentication errors with exit code zero, plus successful PONG.
LinuxServer multi-image definitions and Kimai
The README converter now retains the full Compose stack instead of only the main image and dependency names. Translation blockers remain until the generic driver supports the full stack. Kimai reuses LinuxServer's own MariaDB image and its /config persistence. Its generated database password matches DATABASE_URL; readiness executes an authenticated SELECT 1 rather than merely checking a port. The app uses Doctrine's automatic server-version detection and allows IPv4 host names for the initial LAN deployment. Configure a specific domain when adding a reverse proxy. Completion prints upstream instructions for creating the first administrator inside the Kimai LXC; no default account is invented.
Kimai is installable but runtime-unvalidated. Diskover remains blocked: its upstream example includes an Elasticsearch dependency and a privileged helper changing host vm.max_map_count. Host kernel settings and Elasticsearch version compatibility need separate review, not silent removal of these requirements.
HAOS One native OCI profile
The community image qweritos/haos-one:latest is installable with the laboratory
adaptation: unprivileged LXC, ostype=unmanaged, nesting=1, keyctl=1, and a
managed /mnt/data volume included in Proxmox backups (32 GB default, 16 GB
minimum). The image's entrypoint, command and stop signal remain imported from
OCI metadata. No Docker daemon or compatibility proxy is installed by ProxMenux;
the nested runtime belongs to the image itself.
The installer asks for explicit acknowledgement of the experimental profile and its inner AppArmor limitations. First boot may pull several images. A bounded 20-minute check reports progress and requires healthy/supported Supervisor, the real Core container rather than the landing page, running CLI/DNS/audio/multicast/ Observer containers, and working Core and Observer HTTP endpoints. The final Core URL is detected on port 80 or 8123 instead of assumed. A first-boot failure preserves the LXC, data and root-only console log for diagnosis; it never reports success. If starting is declined, no unverified URL is printed.
This installer path and the rolling latest image remain runtime-unvalidated. The historical lab evidence stays in the template; it is not a guarantee for new releases. Full backup restore, outer-image replacement, USB and multicast discovery still need explicit tests. See the upstream project: https://github.com/qweritos/haos-one
Native Compose resource limits
mem_limit now supplies the editable Proxmox RAM default (MiB rounded upwards,
minimum 16 MiB). A conflicting deploy.resources.limits.memory remains blocked
for review. Swap is still an independent Proxmox setting, not a claim of exact
Docker swap defaults. ulimits maps soft/hard values to native lxc.prlimit.*;
-1 becomes unlimited. Invalid resource names, malformed values, duplicate
remote entries and soft limits greater than hard limits are rejected. The
installation summary shows these limits, including per-service stack limits.
No host sysctls, service-manager limits or privileges are silently changed to
make a requested limit succeed. LXC/kernel restrictions still apply. In
particular nproc is per real UID, not per container, and is not a replacement
for a cgroup PID limit. Reference: https://linuxcontainers.org/lxc/manpages/man5/lxc.container.conf.5.html
Diskover and RagFlow no longer carry the generic mem_limit/ulimits translation
blockers, but remain unavailable for automatic installation until their other
dependencies, healthchecks and host requirements are adapted. Diskover retains
Elasticsearch 7.17.22 in original_compose; the normalized candidate follows the
catalog's latest policy. Compatibility and tag availability therefore require
review before promotion, not an unverified Elasticsearch upgrade. The full Compose is now preserved in the active JSON,
as it already is when regenerating from the LinuxServer README.
Host monitors: experimental native profile
Glances offers an isolated alternative when host access is declined: an
unprivileged LXC with its own IP, default AppArmor, no host mounts, and no extra
capabilities. It monitors only itself, not Proxmox. The final summary states this
scope explicitly. GLANCES_OPT=-w remains automatic in either mode. Netdata's
host profile still requires acceptance; this fallback applies only to Glances.
Glances and Netdata have an installable host_monitor profile, tested on amd64
Proxmox 9.2.18 on 2026-09-14. Both expose host CPU/RAM/process metrics and survive
a shutdown/start cycle. Netdata also exposes LXC cgroup charts and preserves its
registry identity in a managed volume. Observed image digests and versions are
recorded in each template; rolling tags and arm64 are not universally validated.
The profile uses a privileged LXC, explicitly inherits host PID and network namespaces, and uses unconfined AppArmor. It requires informed consent; a compromised monitor could affect the host. Its endpoint uses the host IP and host firewall, with a port-conflict check before image download. Do not expose these unauthenticated dashboards to untrusted networks.
Only the monitor's LXCFS mount hook is cleared, to avoid reporting container
CPU/RAM limits as host metrics. Proxmox pre-start/autodev/post-stop hooks are
retained. No Docker socket or host root filesystem is mounted. Netdata's native
/host paths receive read-only proc/sys/identity mounts, and its three private
data directories remain managed volumes included in backup. Full filesystem,
SMART, Docker inventory and GPU monitoring are not implied by this profile.
The host cgroup mount is explicitly bound read-only below /host/sys/fs/cgroup.
CPU consumption is bounded with cpulimit rather than a restricted CPU affinity,
so the monitor sees the host's real processor count.
Proxmox does not accept lxc.namespace.share.* directly in CT configuration.
The installer uses supported lxc.include referencing the static companion
/etc/pve/lxc/proxmenux-host-monitor. This file persists across host reboots but
is NOT included in a CT vzdump. Preserve/recreate it when restoring on another
host, in addition to restoring managed volumes. Full restore/image replacement
have not been tested. No custom supervisor or image entrypoint is introduced.
DeepSeek OCR remains deferred at the user's request: registry inspection on
2026-09-14 found only v2.2.0 for both IceWhaleTech images and no latest tag.
No fixed-version exception or deployment has been introduced.