@@ -162,7 +162,7 @@ public function deployExAppHarp(ExApp $exApp, DaemonConfig $daemonConfig, array
162162 'mount_points ' => $ mountPoints ,
163163 'start_container ' => true
164164 ];
165-
165+
166166 if (isset ($ params ['container_params ' ]['resourceLimits ' ]) && !empty ($ params ['container_params ' ]['resourceLimits ' ])) {
167167 $ createPayload ['resource_limits ' ] = $ params ['container_params ' ]['resourceLimits ' ];
168168 }
@@ -529,12 +529,31 @@ public function createContainer(string $dockerUrl, string $imageId, DaemonConfig
529529 $ containerParams ['NetworkingConfig ' ] = $ networkingConfig ;
530530 }
531531
532+ $ isPodman = $ this ->isPodman ($ dockerUrl );
532533 if (isset ($ params ['computeDevice ' ])) {
533534 if ($ params ['computeDevice ' ]['id ' ] === 'cuda ' ) {
534- if (isset ($ params ['deviceRequests ' ])) {
535- $ containerParams ['HostConfig ' ]['DeviceRequests ' ] = $ params ['deviceRequests ' ];
535+ if ($ isPodman === true ) {
536+ // Podman 5.4+: use Docker-compat DeviceRequests with CDI driver.
537+ $ selectors = $ params ['cdiDevices ' ] ?? ['nvidia.com/gpu=all ' ];
538+ $ containerParams ['HostConfig ' ]['DeviceRequests ' ] = [[
539+ 'Driver ' => 'cdi ' ,
540+ 'DeviceIDs ' => $ selectors ,
541+ ]];
542+ // Remove NVIDIA_* envs to avoid hook/handoff conflicts under CDI
543+ if (!empty ($ containerParams ['Env ' ])) {
544+ $ containerParams ['Env ' ] = array_values (array_filter (
545+ $ containerParams ['Env ' ],
546+ fn (string $ e ) =>
547+ !str_starts_with ($ e , 'NVIDIA_VISIBLE_DEVICES= ' ) &&
548+ !str_starts_with ($ e , 'NVIDIA_DRIVER_CAPABILITIES= ' )
549+ ));
550+ }
536551 } else {
537- $ containerParams ['HostConfig ' ]['DeviceRequests ' ] = $ this ->buildDefaultGPUDeviceRequests ();
552+ if (isset ($ params ['deviceRequests ' ])) {
553+ $ containerParams ['HostConfig ' ]['DeviceRequests ' ] = $ params ['deviceRequests ' ];
554+ } else {
555+ $ containerParams ['HostConfig ' ]['DeviceRequests ' ] = $ this ->buildDefaultGPUDeviceRequests ();
556+ }
538557 }
539558 }
540559 if ($ params ['computeDevice ' ]['id ' ] === 'rocm ' ) {
@@ -1351,4 +1370,29 @@ private function isLocalSocket(string $host): bool {
13511370 }
13521371 return $ isLocalPath ;
13531372 }
1373+
1374+ private function buildCompatUrlRaw (string $ dockerUrl , string $ route ): string {
1375+ return rtrim ($ dockerUrl , '/ ' ) . '/ ' . ltrim ($ route , '/ ' );
1376+ }
1377+
1378+ private function isPodman (string $ dockerUrl ): bool {
1379+ try {
1380+ // _ping is unversioned and returns headers we can key on
1381+ $ resp = $ this ->guzzleClient ->head ($ this ->buildCompatUrlRaw ($ dockerUrl , '_ping ' ));
1382+ if ($ resp ->hasHeader ('Libpod-Api-Version ' )) {
1383+ return true ;
1384+ }
1385+ $ server = $ resp ->getHeaderLine ('Server ' );
1386+ if ($ server && stripos ($ server , 'Libpod ' ) !== false ) {
1387+ return true ;
1388+ }
1389+ // fallback: /version body
1390+ $ resp = $ this ->guzzleClient ->get ($ this ->buildCompatUrlRaw ($ dockerUrl , 'version ' ));
1391+ $ body = json_decode ((string ) $ resp ->getBody (), true );
1392+ $ platformName = $ body ['Platform ' ]['Name ' ] ?? '' ;
1393+ return stripos ($ platformName , 'podman ' ) !== false ;
1394+ } catch (\Throwable $ e ) {
1395+ return false ;
1396+ }
1397+ }
13541398}
0 commit comments