~/blog/6

$ cat post-6.log

Published

#Docker #Podman #Azure #Containers

Why Containers?

"It works on my machine" used to be a running joke in development. Environmental differences between development, testing, and production caused countless hours of debugging. A library version mismatch here, a missing dependency there, an environment variable that's set differently—any of these could break a deployment.

Containers promised a solution: package your application with all its dependencies into a standardized unit that runs identically everywhere. When we started modernizing EUCARIS, containerization wasn't optional—it was essential for consistent deployments across multiple European countries with different hosting environments.

Starting with Docker

Docker was the obvious starting point. The ecosystem was mature, documentation was abundant, and the developer experience was smooth. Writing a Dockerfile for our .NET 8 application was straightforward:

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApp.csproj", "./"]
RUN dotnet restore
COPY . .
RUN dotnet build -c Release -o /app/build

FROM build AS publish
RUN dotnet publish -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyApp.dll"]

Multi-stage builds kept our final images small—only the runtime and published artifacts, not the SDK. Local development became consistent: docker-compose up and everyone had an identical environment, regardless of their OS.

Discovering Podman

Docker served us well, but we started encountering friction. Docker Desktop's licensing changes for larger organizations raised questions. The daemon architecture meant running containers required elevated privileges. And on our CI/CD agents, we wanted something lighter.

Enter Podman. It's daemonless, rootless, and Docker-compatible. The command-line interface mirrors Docker almost exactly—alias docker=podman just works for most use cases. Podman runs containers without requiring a background daemon, which made it perfect for our Azure DevOps build agents.

The transition was smooth. Our Dockerfiles worked with Podman unchanged. The biggest difference was the lack of Docker Compose support initially, but podman-compose filled that gap. For production workloads, Podman's rootless containers provided better security—no root access needed to run containers.

Docker vs Podman: Practical Comparison

Aspect Docker Podman
Architecture Client-server (daemon) Daemonless
Root access Required Optional (rootless mode)
CLI compatibility Docker CLI Docker-compatible
Compose support Native Via podman-compose
Systemd integration Third-party Native
License Docker Desktop has commercial restrictions Fully open source (Apache 2.0)

Azure Container Apps: Managed Simplicity

Building and running containers locally is one thing. Managing them in production—scaling, load balancing, rolling updates, health checks, logging—is another level of complexity. We didn't want to manage Kubernetes clusters. We wanted something simpler.

Azure Container Apps gave us managed container hosting without Kubernetes complexity. We provide a container image, specify scaling rules, and Azure handles the rest. Autoscaling based on HTTP traffic or custom metrics? Built-in. Blue-green deployments? Revision management handles it. Ingress and certificates? Configured through the portal or CLI.

Our deployment workflow became: build container with Podman in CI/CD, push to Azure Container Registry, deploy to Azure Container Apps. The service auto-scales from zero to dozens of instances based on load. We only pay for what we use. No cluster management, no node patching, no Kubernetes YAML engineering.

Real-World Considerations

  • Image size matters: Multi-stage builds and Alpine-based images keep containers lean. Smaller images mean faster deployments
  • Layer caching: Order Dockerfile instructions by frequency of change. Dependency restoration should happen before copying source code
  • Health checks: Containers should expose health endpoints. Azure Container Apps uses them to know when to route traffic
  • Secrets management: Use Azure Key Vault integration. Never bake secrets into images
  • Logging: Structured logging to stdout/stderr. Azure Container Apps forwards logs to Application Insights or Log Analytics
  • Stateless design: Container instances are ephemeral. Store state in databases or external storage, not in the container filesystem

The Verdict

Containerization transformed our deployment process. Consistency between environments, simplified infrastructure, and faster iteration cycles. Docker gave us the ecosystem and learning resources. Podman provided security and licensing clarity. Azure Container Apps delivered managed hosting without operational burden.

The tools matter less than the principles: package your application with its dependencies, design for ephemerality, and embrace infrastructure as code. Whether you choose Docker, Podman, or another tool, containers are a foundational technology for modern software delivery.

← Back to blog overview