dotnet-artifacts-output
dotnet-artifacts-output
Reference guide for the .NET SDK artifacts output layout, which centralizes build outputs (bin/, obj/, publish/, package/) into a single artifacts/ directory at the repo root. Available since .NET 8 as an opt-in feature. Recommended for new projects; evaluate tradeoffs before migrating existing projects.
Prerequisites: Run [skill:dotnet-version-detection] first to confirm .NET 8+ SDK -- artifacts output layout is not available in earlier SDK versions.
Scope
- UseArtifactsOutput opt-in and ArtifactsPath configuration
- Centralized build output layout (artifacts/bin/, artifacts/obj/, artifacts/publish/)
- Impact on CI artifact upload paths and Docker builds
- Migration tradeoffs for existing projects
Out of scope
- Source tree organization (.sln, .csproj, src/, tests/) -- see [skill:dotnet-project-structure]
Cross-references: [skill:dotnet-project-structure] for solution layout, [skill:dotnet-containers] for Dockerfile path adjustments, [skill:dotnet-gha-build-test] for CI artifact upload paths, [skill:dotnet-scaffold-project] for generating new projects with artifacts output enabled.
Why Use Artifacts Output
Traditional .NET build output scatters bin/ and obj/ directories throughout the source tree, one per project. The artifacts output layout consolidates all build outputs under a single artifacts/ directory next to Directory.Build.props.
Benefits:
- Simpler
.gitignore-- oneartifacts/entry replaces per-projectbin/andobj/entries - Easier clean builds -- delete one directory instead of hunting for scattered
bin//obj/folders - Predictable output paths -- tooling can anticipate where to find build outputs without traversing the source tree
- Cleaner source tree -- no build artifacts mixed into project directories
Tradeoffs:
- Breaking path assumptions -- existing CI pipelines, Dockerfiles, and tooling that reference
bin/Debug/net10.0/paths must be updated - IDE/tool compatibility -- some older tools may not resolve the new output paths correctly
- Migration effort -- existing projects require updating all hardcoded output path references
Enabling Artifacts Output
Add UseArtifactsOutput to your Directory.Build.props at the repo root:
<Project>
<PropertyGroup>
<UseArtifactsOutput>true</UseArtifactsOutput>
</PropertyGroup>
</Project>
Alternatively, generate a new Directory.Build.props with artifacts output pre-configured:
dotnet new buildprops --use-artifacts
This creates:
<Project>
<PropertyGroup>
<ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>
</PropertyGroup>
</Project>
Setting ArtifactsPath directly is equivalent to UseArtifactsOutput=true and additionally lets you customize the root directory location.
Output Path Structure
All build outputs are organized under artifacts/ with three levels: output type, project name, and pivot (configuration/TFM/RID).
artifacts/
bin/
MyApp/
debug/ # Single-targeted project
debug_net10.0/ # Multi-targeted project
release_linux-x64/ # RID-specific build
MyApp.Core/
debug/
obj/
MyApp/
debug/
publish/
MyApp/
release/ # dotnet publish output
release_linux-x64/ # RID-specific publish
package/
release/ # NuGet .nupkg files (no project subfolder)
Output Type Directories
| Directory | Contents | Traditional equivalent |
|---|---|---|
artifacts/bin/ |
Compiled assemblies and dependencies | <project>/bin/ |
artifacts/obj/ |
Intermediate build files, generated code | <project>/obj/ |
artifacts/publish/ |
Published application output | <project>/bin/<config>/<tfm>/publish/ |
artifacts/package/ |
NuGet packages (.nupkg, .snupkg) |
<project>/bin/<config>/ |
Pivot Naming
The pivot subfolder combines configuration, TFM, and RID joined by underscores. Components that are not present are omitted:
| Scenario | Pivot | Full path example |
|---|---|---|
| Single-targeted, debug | debug |
artifacts/bin/MyApp/debug/ |
| Multi-targeted, debug | debug_net10.0 |
artifacts/bin/MyApp/debug_net10.0/ |
| Release, RID-specific | release_linux-x64 |
artifacts/bin/MyApp/release_linux-x64/ |
| Package output | release |
artifacts/package/release/ |
Note: artifacts/package/ omits the project name subfolder. The pivot includes only the configuration.
Customizing the Artifacts Path
Custom Root Directory
Set ArtifactsPath to change the root location:
<PropertyGroup>
<ArtifactsPath>$(MSBuildThisFileDirectory).output</ArtifactsPath>
</PropertyGroup>
This places all build outputs under .output/ instead of artifacts/.
Custom Pivot
Customize the pivot subfolder naming with ArtifactsPivots:
<PropertyGroup>
<ArtifactsPivots>$(ArtifactsPivots)_MyCustomPivot</ArtifactsPivots>
</PropertyGroup>
Impact on .gitignore
With artifacts output enabled, simplify .gitignore:
# Artifacts output layout (replaces per-project bin/ and obj/ entries)
artifacts/
This single entry replaces the traditional pattern:
# Traditional layout (no longer needed with artifacts output)
[Bb]in/
[Oo]bj/
If using a custom ArtifactsPath, update the .gitignore entry to match.
Impact on Dockerfiles
Multi-stage Dockerfiles that copy build output must reference the new path structure. See [skill:dotnet-containers] for full Dockerfile patterns.
Traditional paths:
COPY /app/src/MyApp/bin/Release/net10.0/publish/ .
Artifacts output paths:
COPY /app/artifacts/publish/MyApp/release/ .
Key differences in Dockerfile paths:
- Output is under
artifacts/publish/notbin/Release/<tfm>/publish/ - Project name becomes a subdirectory under the output type
- Configuration pivot is lowercase (
releasenotRelease) - TFM is omitted from single-targeted project pivots
Impact on CI Pipelines
CI workflows that upload build artifacts or reference output paths must be updated. See [skill:dotnet-gha-build-test] for full CI workflow patterns.
GitHub Actions -- upload build output:
- name: Publish
run: dotnet publish src/MyApp/MyApp.csproj -c Release
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: app
path: artifacts/publish/MyApp/release/
GitHub Actions -- upload NuGet packages:
- name: Pack
run: dotnet pack -c Release
- name: Upload packages
uses: actions/upload-artifact@v4
with:
name: packages
path: artifacts/package/release/*.nupkg
Azure DevOps -- publish artifacts:
- script: dotnet publish src/MyApp/MyApp.csproj -c Release
displayName: 'Publish'
- task: PublishPipelineArtifact@1
inputs:
targetPath: 'artifacts/publish/MyApp/release/'
artifact: 'app'
Migration Checklist
When enabling artifacts output on an existing project:
- Add
UseArtifactsOutputtoDirectory.Build.props - Update
.gitignore-- replace[Bb]in/and[Oo]bj/withartifacts/ - Update Dockerfiles -- change all
COPY --from=buildpaths to useartifacts/structure - Update CI pipelines -- fix artifact upload paths, test result paths, and publish paths
- Update local scripts -- fix any shell scripts that reference
bin/orobj/paths - Clean old output -- delete existing
bin/andobj/directories from all projects - Verify builds -- run
dotnet buildanddotnet publishto confirm output appears underartifacts/ - Verify tests -- run
dotnet testto confirm test execution with new paths
Agent Gotchas
- Do not hardcode TFM in artifacts output paths for single-targeted projects. The pivot for single-targeted projects is just the configuration (e.g.,
debug), notdebug_net10.0. Multi-targeted projects include the TFM in the pivot. - Do not use capitalized configuration names in artifacts paths. The artifacts layout uses lowercase pivots (
debug,release), not the traditional capitalized names (Debug,Release). - Do not assume
artifacts/package/has a project name subfolder. Unlikebin/,obj/, andpublish/, thepackage/output type omits the project name level. Packages appear directly underartifacts/package/<config>/. - Do not enable artifacts output without updating Dockerfiles and CI pipelines first. Path changes will break
COPY --from=builddirectives and artifact upload steps that reference traditionalbin/paths. - Do not present artifacts output as the default .NET layout. It is opt-in since .NET 8 and remains opt-in. Recommend it for new projects; for existing projects, evaluate the migration effort against the benefits.
References
More from novotnyllc/dotnet-artisan
dotnet-csharp
Baseline C# skill loaded for every .NET code path. Guides language patterns (records, pattern matching, primary constructors, C# 8-15), coding standards, async/await, DI, LINQ, serialization, domain modeling, concurrency, Roslyn analyzers, globalization, native interop (P/Invoke, LibraryImport, ComWrappers), WASM interop (JSImport/JSExport), and type design. Spans 25 topics. Do not use for ASP.NET endpoint architecture, UI framework patterns, or CI/CD guidance.
128dotnet-ui
Builds .NET UI apps across Blazor (Server, WASM, Hybrid, Auto), MAUI (XAML, MVVM, Shell, Native AOT), Uno Platform (MVUX, Extensions, Toolkit), WPF (.NET 8+, Fluent theme), WinUI 3 (Windows App SDK, MSIX, Mica/Acrylic, adaptive layout), and WinForms (high-DPI, dark mode) with JS interop, accessibility (SemanticProperties, ARIA), localization (.resx, RTL), platform bindings (Java.Interop, ObjCRuntime), and framework selection. Spans 20 topic areas. Do not use for backend API design or CI/CD pipelines.
99dotnet-api
Builds ASP.NET Core APIs, EF Core data access, gRPC, SignalR, and backend services with middleware, security (OAuth, JWT, OWASP), resilience, messaging, OpenAPI, .NET Aspire, Semantic Kernel, HybridCache, YARP reverse proxy, output caching, Office documents (Excel, Word, PowerPoint), PDF, and architecture patterns. Spans 32 topic areas. Do not use for UI rendering patterns or CI/CD pipeline authoring.
90dotnet-testing
Defines .NET test strategy and implementation patterns across xUnit v3 (Facts, Theories, fixtures, IAsyncLifetime), integration testing (WebApplicationFactory, Testcontainers), Aspire testing (DistributedApplicationTestingBuilder), snapshot testing (Verify, scrubbing), Playwright E2E browser automation, BenchmarkDotNet microbenchmarks, code coverage (Coverlet), mutation testing (Stryker.NET), UI testing (page objects, selectors), and AOT WASM test compilation. Spans 13 topic areas. Do not use for production API architecture or CI workflow authoring.
86dotnet-advisor
Routes .NET/C# requests to the correct domain skill and loads coding standards as baseline for all code paths. Determines whether the task needs API, UI, testing, devops, tooling, or debugging guidance based on prompt analysis and project signals, then invokes skills in the right order. Always invoked after [skill:using-dotnet] detects .NET intent. Do not use for deep API, UI, testing, devops, tooling, or debugging implementation guidance.
60dotnet-debugging
Debugs Windows and Linux/macOS applications (native, .NET/CLR, mixed-mode) with WinDbg MCP (crash dumps, !analyze, !syncblk, !dlk, !runaway, !dumpheap, !gcroot, BSOD), dotnet-dump, lldb with SOS, createdump, and container diagnostics (Docker, Kubernetes). Hang/deadlock diagnosis, high CPU triage, memory leak investigation, kernel debugging, and dotnet-monitor for production. Spans 17 topic areas. Do not use for routine .NET SDK profiling, benchmark design, or CI test debugging.
57