Skip to content

How to Publish a .NET Application from a Batch Script

Learn how to automate .NET application publishing using Batch Script with dotnet publish, covering framework-dependent vs. self-contained deployments, runtime identifiers, single-file publishing, and production-ready workflows.

Publishing a .NET application creates a self-contained, deployable package that includes the compiled application, its dependencies, configuration files, and optionally the .NET runtime itself. Unlike a simple build, publishing produces output that is ready to be copied directly to a server and run without requiring the .NET SDK on the target machine. Automating this process through Batch Script ensures consistent, reproducible deployments.

In this guide, we will explore how to publish .NET applications from a Batch Script using the dotnet publish command, covering framework-dependent and self-contained deployments, runtime identifiers, single-file publishing, and production-grade workflows.

Understanding Publish Modes

Mode Flag Runtime Required on Server? Output Size
Framework-dependent (default) Yes (.NET Runtime) Small
Self-contained --self-contained No Large (includes runtime)
Single-file /p:PublishSingleFile=true Depends on mode Single executable
ReadyToRun (R2R) /p:PublishReadyToRun=true Depends on mode Larger, faster startup

Method 1: Basic Framework-Dependent Publish

@echo off
setlocal

set "project=MyWebApp"
set "config=Release"
set "output=publish"

:: Verify dotnet CLI is available
where dotnet >nul 2>&1
if %errorlevel% neq 0 (
    echo [ERROR] dotnet CLI not found. Install the .NET SDK.
    pause
    exit /b 1
)

echo Publishing %project% (%config%^)...

:: Clean output
if exist "%output%" rmdir /s /q "%output%"

dotnet publish "%project%" -c %config% -o "%output%"

if %errorlevel%==0 (
    echo.
    echo [SUCCESS] Published to: %output%
    echo.
    echo Contents:
    dir /b "%output%\*.dll" "%output%\*.exe" "%output%\*.json" 2>nul
) else (
    echo.
    echo [ERROR] Publish failed.
    exit /b 1
)

pause

Method 2: Self-Contained Publish

Include the .NET runtime so the target server does not need .NET installed:

@echo off
setlocal

set "project=MyWebApp"
set "config=Release"
set "rid=win-x64"
set "output=publish\%rid%"

echo Publishing self-contained for %rid%...

if exist "%output%" rmdir /s /q "%output%"

dotnet publish "%project%" -c %config% -r %rid% --self-contained -o "%output%"

if %errorlevel%==0 (
    echo.
    echo [SUCCESS] Self-contained publish complete.
    echo Output: %output%
    echo.
    echo The application can run on any Windows x64 machine without .NET installed.
) else (
    echo [ERROR] Publish failed.
    exit /b 1
)

pause

Common Runtime Identifiers (RIDs)

RID Platform
win-x64 Windows 64-bit
win-x86 Windows 32-bit
win-arm64 Windows ARM64
linux-x64 Linux 64-bit
linux-arm64 Linux ARM64
osx-x64 macOS Intel
osx-arm64 macOS Apple Silicon

Method 3: Single-File Publish

Create a single executable that bundles everything:

@echo off
setlocal enabledelayedexpansion

set "project=MyConsoleApp"
set "config=Release"
set "rid=win-x64"
set "output=publish\single"

echo Publishing as single file...

if exist "%output%" rmdir /s /q "%output%"

dotnet publish "%project%" -c %config% -r %rid% --self-contained ^
    /p:PublishSingleFile=true ^
    /p:PublishTrimmed=true ^
    /p:IncludeNativeLibrariesForSelfExtract=true ^
    -o "%output%"

if !errorlevel!==0 (
    echo.
    echo [SUCCESS] Single file published.
    for %%F in ("%output%\*.exe") do (
        echo   File: %%~nxF
        echo   Size: %%~zF bytes
    )
) else (
    echo [ERROR] Failed.
    exit /b 1
)

pause

Method 4: Full Publish Pipeline with Versioning

@echo off
setlocal enabledelayedexpansion

:: Configuration
set "project=MyWebApp"
set "config=Release"
set "publish_dir=artifacts\publish"
set "package_dir=artifacts\packages"

:: Verify dotnet CLI
where dotnet >nul 2>&1
if %errorlevel% neq 0 (
    echo [ERROR] dotnet CLI not found.
    exit /b 1
)

echo =============================================
echo   .NET PUBLISH PIPELINE
echo =============================================
echo.

:: Version
set "version=1.0.0"
if exist VERSION (
    set /p "version=" < VERSION
)
set "build=0"
if exist BUILD_NUMBER (
    set /p "build=" < BUILD_NUMBER
)
set /a build+=1
>BUILD_NUMBER echo !build!

set "full_version=!version!.!build!"
echo Version: !full_version!
echo.

:: Stage 1: Clean
echo [1/4] Cleaning...
if exist "%publish_dir%" rmdir /s /q "%publish_dir%"
if exist "%package_dir%" rmdir /s /q "%package_dir%"
mkdir "%publish_dir%"
mkdir "%package_dir%"

dotnet clean "%project%" -c %config% --verbosity quiet >nul 2>&1
echo       Done.

:: Stage 2: Test
echo [2/4] Running tests...
if exist "%project%.Tests" (
    dotnet test "%project%.Tests" -c %config% --verbosity quiet
    if !errorlevel! neq 0 (
        echo       [FAIL] Tests failed. Aborting publish.
        exit /b 1
    )
    echo       Done.
) else (
    echo       No test project found (%project%.Tests^). Skipping.
)

:: Stage 3: Publish
echo [3/4] Publishing...
dotnet publish "%project%" -c %config% -o "%publish_dir%" ^
    /p:Version=!full_version! ^
    /p:AssemblyVersion=!full_version! ^
    /p:InformationalVersion=!full_version!

if !errorlevel! neq 0 (
    echo       [FAIL] Publish failed.
    exit /b 1
)
echo       Done.

:: Add build info
for /f "tokens=2 delims==" %%T in ('wmic os get LocalDateTime /value') do set "dt=%%T"
set "build_date=%dt:~0,4%-%dt:~4,2%-%dt:~6,2% %dt:~8,2%:%dt:~10,2%:%dt:~12,2%"

(
echo version=!full_version!
echo published=!build_date!
echo machine=%COMPUTERNAME%
echo config=%config%
) > "%publish_dir%\build_info.txt"

:: Stage 4: Package
echo [4/4] Packaging...
set "package=%package_dir%\%project%_v!full_version!.zip"

powershell -NoProfile -Command ^
    "try { Compress-Archive -Path '%publish_dir%\*' -DestinationPath '!package!' -Force -ErrorAction Stop; exit 0 }" ^
    "catch { Write-Host ('[ERROR] ' + $_.Exception.Message); exit 1 }"

if !errorlevel! neq 0 (
    echo       [FAIL] Packaging failed.
    exit /b 1
)

for %%F in ("!package!") do set "size=%%~zF"

echo.
echo =============================================
echo   PUBLISH COMPLETE
echo   Version: !full_version!
echo   Output:  %publish_dir%
echo   Package: !package!
echo   Size:    !size! bytes
echo =============================================

pause

Method 5: Multi-Platform Publish

Build for multiple target platforms in one script:

@echo off
setlocal enabledelayedexpansion

set "project=MyToolApp"
set "config=Release"
set "output_base=artifacts"

set "platform_count=3"
set "platforms[0]=win-x64"
set "platforms[1]=linux-x64"
set "platforms[2]=osx-x64"

echo =============================================
echo   MULTI-PLATFORM PUBLISH
echo =============================================
echo.

set "success=0"
set "failed=0"
set /a last=platform_count - 1

for /L %%i in (0,1,!last!) do (
    set "rid=!platforms[%%i]!"
    set "out=%output_base%\!rid!"

    echo Building for !rid!...

    if exist "!out!" rmdir /s /q "!out!"

    dotnet publish "%project%" -c %config% -r !rid! --self-contained ^
        /p:PublishSingleFile=true -o "!out!" --verbosity quiet

    if !errorlevel!==0 (
        set /a success+=1
        for %%F in ("!out!\%project%*") do echo   [OK] %%~nxF (%%~zF bytes^)
    ) else (
        set /a failed+=1
        echo   [FAIL]
    )
)

echo.
echo =============================================
echo   RESULTS: !success! succeeded, !failed! failed
echo =============================================

if !failed! gtr 0 exit /b 1
pause

Docker Publish

Publish and create a Docker image:

@echo off
setlocal

set "project=MyWebAPI"
set "image_name=mywebapi"
set "publish_dir=publish\docker"

set "version=latest"
if exist VERSION (
    set /p "version=" < VERSION
)

:: Verify Docker is available
where docker >nul 2>&1
if %errorlevel% neq 0 (
    echo [ERROR] Docker not found.
    pause
    exit /b 1
)

if not exist "Dockerfile" (
    echo [ERROR] Dockerfile not found in the current directory.
    pause
    exit /b 1
)

echo Publishing for Docker...

:: Publish for Linux
if exist "%publish_dir%" rmdir /s /q "%publish_dir%"

dotnet publish "%project%" -c Release -r linux-x64 --self-contained -o "%publish_dir%"
if %errorlevel% neq 0 (
    echo [ERROR] dotnet publish failed.
    exit /b 1
)

:: Build Docker image
docker build -t %image_name%:%version% .

if %errorlevel%==0 (
    echo [SUCCESS] Docker image: %image_name%:%version%
) else (
    echo [ERROR] Docker build failed.
    exit /b 1
)

pause

Common Mistakes

The Wrong Way: Publishing Debug Configuration

:: WRONG - Debug builds include debug symbols, are not optimized
dotnet publish MyApp -c Debug -o publish

Output Concern: Debug configuration includes unoptimized code, debug symbols, and potentially development-only settings. Always publish with -c Release for production deployments.

The Wrong Way: Not Specifying Output Directory

:: PROBLEMATIC - Publishes to a deep nested default path
dotnet publish MyApp -c Release
:: Output goes to bin\Release\net8.0\publish\ (hard to find)

Always use -o publish (or similar) to specify a clear, predictable output location.

Best Practices

  1. Always publish in Release mode: -c Release enables optimizations and excludes debug artifacts.
  2. Specify the output directory: Use -o for a predictable deployment source.
  3. Embed version information: Use /p:Version=x.y.z to stamp assemblies with the release version.
  4. Use self-contained for portability: Include the runtime when the target server's .NET version is uncertain.
  5. Test after publishing: Run the published output locally before deploying to production.

Conclusion

Publishing a .NET application from a Batch Script is handled by the dotnet publish command, which produces a deployment-ready package from the project source. By choosing between framework-dependent and self-contained modes, enabling single-file publishing for simpler distribution, and embedding version information into the assemblies, teams create professional deployment artifacts. The publish pipeline pattern (clean, test, publish, package) ensures that every published artifact is verified, versioned, and ready for deployment to any target environment.

Navigation