You drop a Containerfile into a project directory and run the build command
The terminal spins for a minute, then stops with a permission error or a missing context warning. You expected a single image file to appear. Instead, you get a wall of layer hashes and a prompt asking about storage drivers. Fedora ships with Podman instead of Docker, and the build process behaves differently than the tutorials you followed last year. You need a reliable way to turn your build instructions into a runnable image without fighting the daemon or guessing at context boundaries.
What's actually happening
Building a container image is not a single compilation step. The engine reads your Containerfile line by line, executes each instruction, and commits the filesystem changes as a read-only layer. Each layer gets a cryptographic hash. When you run the build again, the engine skips any layer whose hash matches a previous run. That caching mechanism is why placing RUN dnf update before COPY . /app breaks your cache every time you change a single source file.
Podman delegates the actual layer assembly to Buildah. Buildah writes directly to the OCI image format without requiring a background daemon. Because Fedora defaults to rootless containers, the image lives in your user directory under ~/.local/share/containers/storage. The build context is the directory you pass as the final argument. Every file in that directory gets sent to the build environment before the first instruction runs. If you point it at your home directory, the build will time out or hit a disk quota.
The storage backend matters. Fedora uses overlay by default, which relies on fuse-overlayfs for rootless operation. If your kernel lacks the required capabilities or your filesystem does not support overlay mounts, the build will fall back to vfs. The vfs driver copies entire layers for every step. It works everywhere, but it consumes significantly more disk space and slows down repeated builds. Check your storage driver with podman info | grep graphDriverName. Switch to overlay when your kernel supports it. Switch to vfs when you are debugging a broken mount namespace or running on an unsupported filesystem.
The fix or how-to
Create a minimal Containerfile to test the pipeline. Keep the instructions in the correct order to preserve layer caching. Place the file in an empty directory to avoid context bloat.
# Start from a lightweight base that matches your target architecture
FROM registry.fedoraproject.org/fedora:40
# Install dependencies in a single RUN to reduce layer count
RUN dnf install -y python3 python3-pip && dnf clean all
# Copy only the requirements file first so dependency installs are cached
COPY requirements.txt /app/requirements.txt
WORKDIR /app
RUN pip install --no-cache-dir -r requirements.txt
# Copy the rest of the application source code
COPY . /app
# Define the default command when the container starts
CMD ["python3", "main.py"]
Run the build command from the directory containing that file. The dot at the end sets the build context.
podman build -f Containerfile -t myapp:latest .
# -f points to the build instructions file
# -t assigns a human-readable name and tag to the resulting image
# . tells podman to use the current directory as the build context
Watch the output. Each STEP corresponds to a line in your Containerfile. If a step says CACHED, the engine reused a previous layer. If it says COMMIT, the engine wrote new filesystem changes to disk. The final line prints the image ID and the tag you assigned.
Add a .dockerignore file to keep the context lean. The build engine ignores everything listed there before transferring files.
.git
node_modules
__pycache__
*.log
.env
# Prevents version control metadata from bloating the context
# Excludes compiled caches and secret files from the image
# Keeps the transfer payload under the default 100MB limit
Run the build again. The context transfer completes in seconds instead of minutes. The layer cache remains intact for dependency installation.
Verify it worked
Confirm the image exists in your local store and matches the expected size.
podman images | grep myapp
# Lists local images and filters for your tag
# Check that the SIZE column matches your expectations
# Verify the CREATED timestamp matches your last successful build
Run a quick smoke test to ensure the entrypoint executes without permission errors.
podman run --rm myapp:latest echo "build succeeded"
# --rm removes the container immediately after it exits
# Verifies that the image boots and the shell environment is intact
# Confirms that the CMD instruction resolves correctly
Run journalctl first. Read the actual error before guessing.
Common pitfalls and what the error looks like
The build will fail if your context directory contains hidden files that exceed the default transfer limit. You will see Error: error building at STEP "COPY . /app": error adding content to store: context canceled. Add a .dockerignore file to exclude node_modules, .git, and large binary caches.
Rootless builds sometimes hit fuse-overlayfs permission errors when copying files owned by root into a user namespace. The terminal prints Error: error creating container storage: mkdir /home/user/.local/share/containers/storage/overlay: permission denied. Run podman system reset to clear a corrupted storage metadata cache, then rebuild. Never edit files in /usr/lib/containers/storage. User configuration belongs in /etc/containers/storage.conf or ~/.config/containers/storage.conf.
Layer ordering mistakes cause silent performance degradation. If you COPY . /app before RUN pip install, the COPY step invalidates the cache on every run. The build takes three minutes instead of ten seconds. Reorder the instructions so file changes happen after dependency installation.
SELinux denials occasionally block file access during the build phase. The output shows Error: error building at STEP "COPY": permission denied. Check the audit log with journalctl -t setroubleshoot. The one-line summary points to the exact file and the missing boolean. Enable the boolean with setsebool -P container_manage_cgroup on if the denial relates to cgroup access. Do not disable SELinux globally. Fix the policy instead.
Trust the package manager. Manual file edits drift, snapshots stay.
When to use this vs alternatives
Use podman build when you are developing locally and need fast, daemonless image assembly with automatic rootless storage. Use buildah directly when you need to manipulate individual layers, commit running containers, or script image creation without a Containerfile. Use docker build when you are locked into a legacy CI pipeline that requires the Docker daemon and socket authentication. Use kaniko or buildkit when you are building inside a CI runner that lacks root privileges or kernel capabilities. Stay on the default Podman workflow if you only need standard OCI image generation for testing and deployment.