container-publish
Dockerfile-less containerization using the .NET 10 SDK container publishing feature. Covers MSBuild properties, chiseled images, multi-arch builds, and registry publishing — all without writing a Dockerfile. Load this skill when the user wants to containerize without a Dockerfile, or mentions "dotnet publish container", "PublishContainer", "ContainerRepository", "ContainerFamily", "chiseled", "distroless", "container publish", "SDK container", "no Dockerfile", or "containerize without Docker".
How do I install this agent skill?
npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill container-publishIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill provides comprehensive documentation for using the .NET 10 SDK's built-in container publishing features without a Dockerfile. It aligns with security best practices by recommending minimal (chiseled) images and non-root execution environments. No malicious patterns or security risks were detected.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
- ZeroLeakspass
Score: 93/100 · 2 sections analyzed
What does this agent skill do?
Container Publishing (No Dockerfile)
Core Principles
- No Dockerfile needed — The .NET 10 SDK builds OCI-compliant container images directly from
dotnet publish /t:PublishContainer. No Dockerfile to write or maintain. - Chiseled images for production — Use
noble-chiseledbase images: no shell, no package manager, 7 Linux components vs 100+. Smallest attack surface. - Non-root by default — .NET 10 container images run as the
appuser automatically. Never override to root in production. - Configuration in the .csproj — All container settings are MSBuild properties, versioned with your project. No separate files to drift.
Patterns
Minimal Container Publish
No project file changes needed. Just publish:
dotnet publish /t:PublishContainer --os linux --arch x64
This creates a container image in your local Docker daemon using the default aspnet:10.0 base image.
Production-Ready .csproj Configuration
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ContainerRepository>mycompany/myapp-api</ContainerRepository>
<ContainerFamily>noble-chiseled</ContainerFamily>
</PropertyGroup>
<ItemGroup>
<ContainerPort Include="8080" Type="tcp" />
<ContainerEnvironmentVariable Include="ASPNETCORE_HTTP_PORTS" Value="8080" />
<ContainerEnvironmentVariable Include="DOTNET_EnableDiagnostics" Value="0" />
<ContainerLabel Include="org.opencontainers.image.vendor" Value="MyCompany" />
</ItemGroup>
</Project>
Publishing to a Registry
Authenticate with docker login first, then specify the registry:
# GitHub Container Registry
docker login ghcr.io
dotnet publish /t:PublishContainer --os linux --arch x64 \
-p ContainerRegistry=ghcr.io \
-p ContainerImageTag=1.0.0
# Azure Container Registry
az acr login --name myregistry
dotnet publish /t:PublishContainer --os linux --arch x64 \
-p ContainerRegistry=myregistry.azurecr.io
# Docker Hub (requires username prefix in repository)
dotnet publish /t:PublishContainer --os linux --arch x64 \
-p ContainerRegistry=docker.io \
-p ContainerRepository=myuser/myapp
Multi-Architecture Images
Build images for multiple platforms with a single publish:
<PropertyGroup>
<RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
<ContainerRuntimeIdentifiers>linux-x64;linux-arm64</ContainerRuntimeIdentifiers>
</PropertyGroup>
dotnet publish /t:PublishContainer
This produces an OCI Image Index — registries serve the correct architecture automatically.
Multiple Tags
# Bash — note the quoting for semicolons
dotnet publish /t:PublishContainer --os linux --arch x64 \
-p ContainerImageTags='"1.0.0;latest"'
Or in the project file:
<ContainerImageTags>1.0.0;latest</ContainerImageTags>
Save as Tarball (No Docker Required)
No container runtime needed on the build machine. Useful for CI scanning:
dotnet publish /t:PublishContainer --os linux --arch x64 \
-p ContainerArchiveOutputPath=./images/myapp.tar.gz
# Scan with Trivy before pushing
trivy image --input ./images/myapp.tar.gz
Chiseled Image Variants
| ContainerFamily | Use Case | Shell | Size |
|---|---|---|---|
| (default) | General purpose (Debian) | Yes | ~220 MB |
noble-chiseled | Production (no shell) | No | ~110 MB |
noble-chiseled-extra | Production with localization (ICU) | No | ~120 MB |
alpine | Small size, has shell | Yes | ~112 MB |
<!-- Standard chiseled (InvariantGlobalization=true) -->
<ContainerFamily>noble-chiseled</ContainerFamily>
<!-- Chiseled with ICU for localization -->
<ContainerFamily>noble-chiseled-extra</ContainerFamily>
For Native AOT, the SDK auto-selects chiseled-aot:
<PublishAot>true</PublishAot>
<!-- SDK picks runtime-deps:10.0-noble-chiseled-aot automatically -->
CI/CD with GitHub Actions
jobs:
publish:
runs-on: ubuntu-latest
permissions:
packages: write
steps:
- uses: actions/checkout@v5
- uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- run: |
dotnet publish src/MyApp.Api/MyApp.Api.csproj \
/t:PublishContainer --os linux --arch x64 \
-p ContainerRegistry=ghcr.io \
-p ContainerRepository=${{ github.repository_owner }}/myapp \
-p ContainerImageTag=${{ github.sha }}
Anti-patterns
Don't Use the Deprecated Property Names
<!-- BAD — ContainerImageName is deprecated -->
<ContainerImageName>myapp</ContainerImageName>
<!-- GOOD — use ContainerRepository -->
<ContainerRepository>myapp</ContainerRepository>
Don't Use PublishProfile=DefaultContainer
# BAD — old approach, inconsistent across project types
dotnet publish -p:PublishProfile=DefaultContainer
# GOOD — use the MSBuild target directly
dotnet publish /t:PublishContainer
Don't Forget to Target Linux
# BAD on Windows — may produce a Windows container
dotnet publish /t:PublishContainer
# GOOD — explicitly target Linux
dotnet publish /t:PublishContainer --os linux --arch x64
Don't Skip Authentication Before Push
# BAD — fails with CONTAINER1013 error
dotnet publish /t:PublishContainer -p ContainerRegistry=ghcr.io
# GOOD — authenticate first
docker login ghcr.io
dotnet publish /t:PublishContainer -p ContainerRegistry=ghcr.io
Don't Use SDK Publishing When You Need OS Packages
<!-- BAD — SDK container publish cannot run apt-get or install native packages -->
<!-- There is no RUN equivalent -->
<!-- GOOD — create a custom base image with a Dockerfile first, then reference it -->
<ContainerBaseImage>myregistry/custom-base:1.0</ContainerBaseImage>
Decision Guide
| Scenario | Recommendation |
|---|---|
| Standard ASP.NET Core API | SDK container publishing with noble-chiseled |
| Worker service / console app | SDK container publishing (native .NET 10 support) |
| Needs native OS packages | Dockerfile (or custom base image + SDK publishing) |
| Azure Functions | Dockerfile (not supported by SDK publishing) |
| CI without Docker daemon | Tarball output with ContainerArchiveOutputPath |
| Multi-arch deployment (x64 + arm64) | ContainerRuntimeIdentifiers property |
| Production image size | noble-chiseled (~110 MB) or Native AOT (~10 MB) |
| Local development | dotnet publish /t:PublishContainer --os linux --arch x64 |
| Registry push | ContainerRegistry + docker login |
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/codewithmukesh/dotnet-claude-kit/container-publish">View container-publish on skillZs</a>