Skip to content

[BUG] podlet rejects long-form port mapping #207

@karnarokEpoch

Description

@karnarokEpoch

Description

Running podlet compose fails when a service uses the long-form port syntax:

ports:
  - mode: ingress
    target: 80
    published: "8080"
    protocol: tcp

Error:

Error:
   0: error converting compose file
   1: error converting compose file into Quadlet files
   2: error converting service `whoami` into a Quadlet container
   3: error converting `ports`
   4: could not convert port to short syntax, port = Port {
          name: None,
          target: 80,
          published: Some(
              Range {
                  start: 8080,
                  end: None,
              },
          ),
          host_ip: None,
          protocol: Some(
              Tcp,
          ),
          app_protocol: None,
          mode: Some(
              Ingress,
          ),
          extensions: {},
      }

Location:
   src/cli/container/quadlet.rs:753

The long-form syntax is valid per the Compose spec
and commonly generated by podman compose config when expanding environment variables or
processing compose files with advanced features.
Podlet currently requires the short form:

ports:
  - "8080:80"

Steps to reproduce

  1. Create the file:
cat > compose.yaml <<'EOF'
services:
  whoami:
    image: docker.io/traefik/whoami:v1.11
    ports:
      - mode: ingress
        target: 80
        published: "8080"
        protocol: tcp
EOF
  1. Convert with podlet:
podman run --rm -v .:/app ghcr.io/containers/podlet compose /app/compose.yaml

Podlet version

$ podman run --rm ghcr.io/containers/podlet --version
podlet 0.3.1

Podman environment

$ podman --version
podman version 4.9.3

$ podman info
host:
  arch: amd64
  buildahVersion: 1.33.7
  cgroupControllers:
  - cpu
  - memory
  - pids
  cgroupManager: systemd
  cgroupVersion: v2
  conmon:
    package: conmon_2.1.10+ds1-1build2_amd64
    path: /usr/bin/conmon
    version: 'conmon version 2.1.10, commit: unknown'
  cpuUtilization:
    idlePercent: 98.91
    systemPercent: 0.43
    userPercent: 0.66
  cpus: 8
  databaseBackend: sqlite
  distribution:
    codename: noble
    distribution: ubuntu
    version: "24.04"
  eventLogger: journald
  freeLocks: 2021
  hostname: DENEC1
  idMappings:
    gidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536
    uidmap:
    - container_id: 0
      host_id: 1000
      size: 1
    - container_id: 1
      host_id: 100000
      size: 65536
  kernel: 6.6.87.2-microsoft-standard-WSL2
  linkmode: dynamic
  logDriver: journald
  memFree: 3947634688
  memTotal: 8114102272
  networkBackend: netavark
  networkBackendInfo:
    backend: netavark
    dns:
      package: aardvark-dns_1.4.0-5_amd64
      path: /usr/lib/podman/aardvark-dns
      version: aardvark-dns 1.4.0
    package: netavark_1.4.0-4_amd64
    path: /usr/lib/podman/netavark
    version: netavark 1.4.0
  ociRuntime:
    name: crun
    package: crun_1.14.1-1_amd64
    path: /usr/bin/crun
    version: |-
      crun version 1.14.1
      commit: de537a7965bfbe9992e2cfae0baeb56a08128171
      rundir: /run/user/1000/crun
      spec: 1.0.0
      +SYSTEMD +SELINUX +APPARMOR +CAP +SECCOMP +EBPF +WASM:wasmedge +YAJL
  os: linux
  pasta:
    executable: /usr/bin/pasta
    package: passt_0.0~git20240220.1e6f92b-1_amd64
    version: |
      pasta unknown version
      Copyright Red Hat
      GNU General Public License, version 2 or later
        <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html>
      This is free software: you are free to change and redistribute it.
      There is NO WARRANTY, to the extent permitted by law.
  remoteSocket:
    exists: false
    path: /run/user/1000/podman/podman.sock
  security:
    apparmorEnabled: false
    capabilities: CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_FOWNER,CAP_FSETID,CAP_KILL,CAP_NET_BIND_SERVICE,CAP_SETFCAP,CAP_SETGID,CAP_SETPCAP,CAP_SETUID,CAP_SYS_CHROOT
    rootless: true
    seccompEnabled: true
    seccompProfilePath: /usr/share/containers/seccomp.json
    selinuxEnabled: false
  serviceIsRemote: false
  slirp4netns:
    executable: /usr/bin/slirp4netns
    package: slirp4netns_1.2.1-1build2_amd64
    version: |-
      slirp4netns version 1.2.1
      commit: 09e31e92fa3d2a1d3ca261adaeb012c8d75a8194
      libslirp: 4.7.0
      SLIRP_CONFIG_VERSION_MAX: 4
      libseccomp: 2.5.5
  swapFree: 2147483648
  swapTotal: 2147483648
  uptime: 1h 46m 50.00s (Approximately 0.04 days)
  variant: ""
plugins:
  authorization: null
  log:
  - k8s-file
  - none
  - passthrough
  - journald
  network:
  - bridge
  - macvlan
  - ipvlan
  volume:
  - local
registries: {}
store:
  configFile: /home/karna/.config/containers/storage.conf
  containerStore:
    number: 1
    paused: 0
    running: 0
    stopped: 1
  graphDriverName: overlay
  graphOptions: {}
  graphRoot: /home/karna/.local/share/containers/storage
  graphRootAllocated: 1081101176832
  graphRootUsed: 17860415488
  graphStatus:
    Backing Filesystem: extfs
    Native Overlay Diff: "true"
    Supports d_type: "true"
    Supports shifting: "false"
    Supports volatile: "true"
    Using metacopy: "false"
  imageCopyTmpDir: /var/tmp
  imageStore:
    number: 145
  runRoot: /run/user/1000/containers
  transientStore: false
  volumePath: /home/karna/.local/share/containers/storage/volumes
version:
  APIVersion: 4.9.3
  Built: 0
  BuiltTime: Thu Jan  1 01:00:00 1970
  GitCommit: ""
  GoVersion: go1.22.2
  Os: linux
  OsArch: linux/amd64
  Version: 4.9.3

Anything else?

Context: Several Compose features are currently not supported by podlet:

  • include directive
  • extends directive
  • profiles directive
  • Environment variable expansion (${VAR:-default})

To work around these limitations, I tried using podman compose config to simplify and expand my compose file before converting with podlet.
This successfully resolves the unsupported features by:

  • Merging included files
  • Expanding extended service configurations
  • Filtering services by profile
  • Resolving environment variable syntax

However, podman compose config automatically converts all port mappings to long-form syntax, which then blocks podlet conversion.
This creates a catch-22 situation where the workaround for one set of limitations introduces a new blocking issue.

Workaround (tedious): Manually convert long-form ports back to short syntax:

# FROM (podman compose config output):
ports:
  - mode: ingress
    target: 80
    published: "8080"
    protocol: tcp

# TO (podlet-compatible):
ports:
  - "8080:80"

Expected behavior: Podlet should accept long-form port syntax and convert it to Quadlet's PublishPort= directive,
extracting the relevant fields (publishedtarget) and ignoring metadata fields like mode and app_protocol.

Suggested conversion logic:

  • published: "8080" + target: 80PublishPort=8080:80
  • host_ip: "127.0.0.1"PublishPort=127.0.0.1:8080:80
  • protocol: udpPublishPort=8080:80/udp
  • Ignore: mode, app_protocol, name (not supported by Podman Quadlet)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions