Problem or Motivation
MikoPBX currently does not detect Podman as a container runtime.
Docker is detected by checking /.dockerenv, while Podman provides the standard /run/.containerenv marker. As a result, an unmodified MikoPBX image running in Podman is not recognized as a container and may attempt to manage networking, firewall, storage, time, and other host-controlled resources.
A real deployment was tested with:
- MikoPBX
2026.3.40
- Ubuntu
26.04 LTS
- Podman
5.7.0
- ARM64/aarch64
- Rootful Podman
--network=host
- Persistent
/cf and /storage bind mounts
The installation works when an artificial Docker marker is mounted:
/var/spool/mikopbx/podman.dockerenv -> /.dockerenv:ro
With this workaround, the following were successfully verified:
- Web interface over HTTPS
- REST API and JWT authentication
- Asterisk 22.8.2
- SIP, SIP TLS, AMI, and Asterisk HTTP interfaces
- Persistent configuration and storage
- Container restart
- ARM64 image compatibility
However, mounting /.dockerenv makes MikoPBX report Podman as Docker and couples Podman deployment to Docker-specific behavior.
Native Podman support is needed without changing the existing Docker detection, Docker provisioning, Docker entrypoint, or Docker deployment behavior.
Proposed Solution
Add Podman as a separate, explicitly detected runtime. Existing Docker behavior should remain unchanged.
1. Add separate Podman detection
Keep the existing method unchanged:
public static function isDocker(): bool
{
return file_exists('/.dockerenv');
}
Add a new method:
public static function isPodman(): bool
{
return file_exists('/run/.containerenv')
&& !file_exists('/.dockerenv');
}
The additional /.dockerenv check provides a safe migration path: existing Podman installations using the compatibility mount remain in Docker mode until that mount is removed.
Update only the generic container checks:
public static function isContainer(): bool
{
return self::isDocker()
|| self::isPodman()
|| self::isLxc();
}
Add explicit Podman handling to capability methods such as:
canManageNetwork()
canManageFirewall()
- reboot and shutdown handling
- storage handling
- DNS and NTP configuration
- network configuration
- container-specific UI restrictions
Existing Docker branches should remain unchanged.
2. Add an independent Podman provisioning provider
Create:
src/Core/System/CloudProvisioning/PodmanCloud.php
PodmanCloud should:
- extend
CloudProvider directly;
- use
System::isPodman();
- have
CloudID = 'PodmanCloud';
- apply environment overrides on every container start;
- apply early Redis, Beanstalk, and NATS port overrides;
- use
ProvisioningConfig::fromEnvironment();
- report the runtime as Podman.
DockerCloud.php should not be changed and PodmanCloud should not inherit from it.
Add Podman as a separate provider in CloudProvisioning:
if (System::isPodman()) {
PodmanCloud::applyPortOverrides();
}
if (System::isDocker()) {
DockerCloud::applyPortOverrides();
}
And:
if (System::isPodman()) {
PodmanCloud::applyEnvironmentOverrides();
} elseif (System::isDocker()) {
DockerCloud::applyEnvironmentOverrides();
} elseif (System::isLxc()) {
LxcCloud::applyProxmoxOverrides();
}
3. Add separate shell detection
Keep is_docker() unchanged and add:
is_podman()
{
[ -f "/run/.containerenv" ] && [ ! -f "/.dockerenv" ]
}
Update the generic detector:
is_container()
{
is_docker || is_podman || is_lxc
}
Update pbx-env-detect to return the actual runtime:
if [ -f "/.dockerenv" ] || [ -n "${DOCKER_CONTAINER:-}" ]; then
echo "docker"
return
fi
if [ -f "/run/.containerenv" ]; then
echo "podman"
return
fi
The same separate Podman detection should be added to pbx-message.
4. Add a dedicated Podman entrypoint
Create:
src/Core/System/RootFS/sbin/podman-entrypoint
The Podman entrypoint should start ContainerEntrypoint.php, monitor the existing reboot/shutdown flags, and provide guarded signal cleanup.
It should prevent recursive cleanup through the EXIT trap:
cleanup_started=0
cleanup()
{
if [ "$cleanup_started" -eq 1 ]; then
return 0
fi
cleanup_started=1
trap - 2 15 1 3 EXIT
/sbin/freestorage 'doNotUnMount'
/sbin/freeupoffload
sleep 4
exit 0
}
The existing docker-entrypoint should remain unchanged.
5. Handle Podman storage separately
In Podman, disk usage should be calculated directly from the /storage bind mount:
if (System::isPodman()) {
$disk['free_space'] = Storage::getFreeSpace('/storage');
} elseif ($disk['sys_disk'] === true) {
// Existing Docker/LXC/VM logic remains unchanged.
}
This prevents container block devices such as /dev/vda2 from being incorrectly transformed into partition names such as /dev/vda24.
6. Add an official Podman Quadlet
Add:
deploy/podman/mikopbx.container
The supported configuration should use:
- Rootful Podman
- systemd Quadlet
Network=host
- Persistent
/cf and /storage
- The dedicated
podman-entrypoint
- Versioned MikoPBX image tags
- systemd restart policy
Example:
[Unit]
Description=MikoPBX Podman
Wants=network-online.target
After=network-online.target
[Container]
Image=ghcr.io/mikopbx/mikopbx:2026.3.40
ContainerName=mikopbx
HostName=mikopbx
Network=host
Entrypoint=/bin/sh
Exec=/sbin/podman-entrypoint
Volume=/var/spool/mikopbx/cf:/cf
Volume=/var/spool/mikopbx/storage:/storage
Environment=SSH_PORT=23
Environment=ID_WWW_USER=1002
Environment=ID_WWW_GROUP=1002
Pull=missing
[Service]
Restart=always
RestartSec=10
TimeoutStartSec=900
TimeoutStopSec=30
[Install]
WantedBy=multi-user.target
No artificial /.dockerenv mount should be required.
7. Expose the runtime in diagnostics
Keep existing Docker fields unchanged and add:
{
"isDocker": false,
"isPodman": true,
"isContainer": true,
"containerRuntime": "podman"
}
The system banner and diagnostic information should display Podman rather than Docker.
8. Add integration coverage
Test matrix:
- Docker amd64
- Docker arm64
- Podman amd64
- Podman arm64
- LXC
- VM or bare metal
Podman acceptance checks:
test -f /run/.containerenv
test ! -f /.dockerenv
test "$(/sbin/pbx-env-detect --nocache --type)" = "podman"
Also verify:
- HTTPS returns HTTP 200
- REST authentication works
- Asterisk starts successfully
- Environment provisioning is applied
/cf and /storage survive restart
- Graceful stop completes without forced
SIGKILL
- No false low-disk-space warning is generated
- The container starts automatically after a host reboot
- Existing Docker tests and behavior remain unchanged
Alternatives Considered
Mounting an artificial /.dockerenv
This workaround was tested and works:
/var/spool/mikopbx/podman.dockerenv -> /.dockerenv:ro
However, it makes MikoPBX identify Podman as Docker and depends on implementation details intended for another runtime.
Extending System::isDocker() to include Podman
For example:
return file_exists('/.dockerenv')
|| file_exists('/run/.containerenv');
This would be a small change, but it would mix two different runtimes, make diagnostics inaccurate, and could unintentionally affect existing Docker behavior.
This alternative is intentionally not proposed.
Reusing or inheriting from DockerCloud
Podman and Docker use similar environment provisioning, but inheriting PodmanCloud from DockerCloud would couple the implementations.
A separate PodmanCloud is preferred so existing Docker behavior can remain unchanged.
Rootless Podman
Rootless Podman was not selected as the initial supported configuration because MikoPBX requires low ports, host networking, SIP/RTP, and predictable UID/GID handling.
The initial supported target should be rootful Podman with systemd and cgroup v2.
Area
Docker / Containers
Additional Context
The test environment was a clean ARM64 virtual machine:
- Ubuntu 26.04 LTS
- Linux aarch64
- Podman 5.7.0
- cgroup v2
- systemd
- crun
- netavark
- MikoPBX 2026.3.40 arm64
The container was started with:
--network=host
--restart=always
/cf bind mount
/storage bind mount
SSH_PORT=23
ID_WWW_USER and ID_WWW_GROUP
Observed results:
- Initial startup completed successfully.
- Restart startup completed in approximately 7.4 seconds.
- Web interface returned HTTP 200 over HTTPS.
- HTTP correctly redirected to HTTPS.
- REST API returned the expected 401 response without authentication.
- JWT login and an authenticated API request succeeded.
- Asterisk and all Monit-managed services were operational.
- Persistent configuration survived container restart.
- TCP ports 23, 80, 443, 5060, 5061, 8088, and 8089 were reachable.
- The ARM64 image worked correctly.
Observed Podman-related issues:
- Native Podman was not detected without mounting
/.dockerenv.
- Container shutdown did not complete within 30 seconds and Podman used
SIGKILL.
- A false low-storage warning was generated for
/dev/vda2, although approximately 18 GB was available.
- The workaround caused diagnostics to report Docker instead of Podman.
Problem or Motivation
MikoPBX currently does not detect Podman as a container runtime.
Docker is detected by checking
/.dockerenv, while Podman provides the standard/run/.containerenvmarker. As a result, an unmodified MikoPBX image running in Podman is not recognized as a container and may attempt to manage networking, firewall, storage, time, and other host-controlled resources.A real deployment was tested with:
2026.3.4026.04 LTS5.7.0--network=host/cfand/storagebind mountsThe installation works when an artificial Docker marker is mounted:
With this workaround, the following were successfully verified:
However, mounting
/.dockerenvmakes MikoPBX report Podman as Docker and couples Podman deployment to Docker-specific behavior.Native Podman support is needed without changing the existing Docker detection, Docker provisioning, Docker entrypoint, or Docker deployment behavior.
Proposed Solution
Add Podman as a separate, explicitly detected runtime. Existing Docker behavior should remain unchanged.
1. Add separate Podman detection
Keep the existing method unchanged:
Add a new method:
The additional
/.dockerenvcheck provides a safe migration path: existing Podman installations using the compatibility mount remain in Docker mode until that mount is removed.Update only the generic container checks:
Add explicit Podman handling to capability methods such as:
canManageNetwork()canManageFirewall()Existing Docker branches should remain unchanged.
2. Add an independent Podman provisioning provider
Create:
PodmanCloudshould:CloudProviderdirectly;System::isPodman();CloudID = 'PodmanCloud';ProvisioningConfig::fromEnvironment();DockerCloud.phpshould not be changed andPodmanCloudshould not inherit from it.Add Podman as a separate provider in
CloudProvisioning:And:
3. Add separate shell detection
Keep
is_docker()unchanged and add:Update the generic detector:
Update
pbx-env-detectto return the actual runtime:The same separate Podman detection should be added to
pbx-message.4. Add a dedicated Podman entrypoint
Create:
The Podman entrypoint should start
ContainerEntrypoint.php, monitor the existing reboot/shutdown flags, and provide guarded signal cleanup.It should prevent recursive cleanup through the
EXITtrap:The existing
docker-entrypointshould remain unchanged.5. Handle Podman storage separately
In Podman, disk usage should be calculated directly from the
/storagebind mount:This prevents container block devices such as
/dev/vda2from being incorrectly transformed into partition names such as/dev/vda24.6. Add an official Podman Quadlet
Add:
The supported configuration should use:
Network=host/cfand/storagepodman-entrypointExample:
No artificial
/.dockerenvmount should be required.7. Expose the runtime in diagnostics
Keep existing Docker fields unchanged and add:
{ "isDocker": false, "isPodman": true, "isContainer": true, "containerRuntime": "podman" }The system banner and diagnostic information should display Podman rather than Docker.
8. Add integration coverage
Test matrix:
Podman acceptance checks:
Also verify:
/cfand/storagesurvive restartSIGKILLAlternatives Considered
Mounting an artificial
/.dockerenvThis workaround was tested and works:
However, it makes MikoPBX identify Podman as Docker and depends on implementation details intended for another runtime.
Extending
System::isDocker()to include PodmanFor example:
This would be a small change, but it would mix two different runtimes, make diagnostics inaccurate, and could unintentionally affect existing Docker behavior.
This alternative is intentionally not proposed.
Reusing or inheriting from
DockerCloudPodman and Docker use similar environment provisioning, but inheriting
PodmanCloudfromDockerCloudwould couple the implementations.A separate
PodmanCloudis preferred so existing Docker behavior can remain unchanged.Rootless Podman
Rootless Podman was not selected as the initial supported configuration because MikoPBX requires low ports, host networking, SIP/RTP, and predictable UID/GID handling.
The initial supported target should be rootful Podman with systemd and cgroup v2.
Area
Docker / Containers
Additional Context
The test environment was a clean ARM64 virtual machine:
The container was started with:
Observed results:
Observed Podman-related issues:
/.dockerenv.SIGKILL./dev/vda2, although approximately 18 GB was available.