Production Deployment (Service/Daemon)

This guide shows how to deploy and update a custom Kestrun PowerShell application to production as a native service/daemon using dotnet kestrun service install and dotnet kestrun service update.

Scope

  • Target: VM or bare-metal hosts.
  • Runtime model: native OS service/daemon.
  • Packaging model: .krpack service package.
  • Not covered: container deployments (Docker/Kubernetes).

Prerequisites

  • .NET 10 runtime/SDK available on the host.
  • Kestrun.Tool installed:
dotnet tool install -g Kestrun.Tool
  • Your app entry script exists (usually Service.ps1 or scripts/start.ps1).
  • If you build packages on the host, ensure the Kestrun PowerShell module is available.
  • The target host account has permissions to install/start services.

Build a .krpack package from your app folder before deployment:

New-KrServicePackage -SourceFolder .\MyServiceApp -OutputPath .\my-service.krpack

The package embeds Service.psd1 metadata and app content used by service install --package and service update --package.

Create Service.psd1

Use New-KrServiceDescriptor to generate a valid descriptor in your service folder. -Path can point to the service directory; Service.psd1 is appended automatically. -EntryPoint must resolve to an existing file under that service directory.

New-KrServiceDescriptor `
  -Path .\MyServiceApp `
  -Name 'my-service' `
  -Description 'Production Kestrun service' `
  -Version 1.2.0 `
  -EntryPoint '.\Service.ps1' `
  -ServiceLogPath '.\logs\service.log' `
  -PreservePaths @('config/production.json') `
  -ApplicationDataFolders @('data/', 'logs/')

Quick verification:

Get-KrServiceDescriptor -Path .\MyServiceApp\Service.psd1

Update descriptor fields later (without changing Name):

Set-KrServiceDescriptor -Path .\MyServiceApp -Version 1.2.1 -Description 'Production Kestrun service (patched)'

Service Descriptor Basics (Service.psd1)

When using package or descriptor-based content deployments, Service.psd1 should define at least:

  • FormatVersion ('1.0')
  • Name
  • Description
  • Version
  • EntryPoint

Optional keys include ServiceLogPath, PreservePaths, and ApplicationDataFolders.

Example:

@{
  FormatVersion = '1.0'
  Name = 'my-service'
  Description = 'Production Kestrun service'
  Version = '1.2.0'
  EntryPoint = './Service.ps1'
  PreservePaths = @(
    'config/production.json'
  )
  ApplicationDataFolders = @(
    'data/'
    'logs/'
  )
}

PreservePaths values must be relative paths that resolve inside the app root (absolute paths and root-escaping paths are rejected). ApplicationDataFolders values follow the same relative-path rules. During service update --package, those paths are staged from the current install and restored after the package content is applied.

Build a .krpack (All New-KrServicePackage Options)

New-KrServicePackage has two parameter sets.

Parameter set 1: package an existing app folder

Required:

  • -SourceFolder <string>: folder that already contains a valid Service.psd1.

Optional:

  • -OutputPath <string>: output package path. If omitted, defaults to <SourceFolderName>.krpack in the current directory.
  • -ExcludeApplicationDataFolders: omit files under descriptor ApplicationDataFolders from the package archive.
  • -ExcludePaths <string[]>: omit additional relative files or folders from the package archive.
  • -Force: overwrite an existing output file.
  • -WhatIf and -Confirm: standard PowerShell safety switches.

Notes:

  • Exclusions apply only to -SourceFolder packaging.
  • Excluded paths must stay under -SourceFolder.
  • Service.psd1 and the descriptor EntryPoint cannot be excluded.
  • -ExcludeApplicationDataFolders does not rewrite Service.psd1; it only removes those files from the archive payload.

Examples:

# Uses default output path: .\MyServiceApp.krpack
New-KrServicePackage -SourceFolder .\MyServiceApp

# Explicit output path
New-KrServicePackage -SourceFolder .\MyServiceApp -OutputPath .\my-service.krpack

# Overwrite existing package
New-KrServicePackage -SourceFolder .\MyServiceApp -OutputPath .\my-service.krpack -Force

# Skip package-time app data plus extra local-only content
New-KrServicePackage `
  -SourceFolder .\MyServiceApp `
  -ExcludeApplicationDataFolders `
  -ExcludePaths @('secrets/dev.json', 'scratch/') `
  -OutputPath .\my-service.krpack

Parameter set 2: package from a script and generate Service.psd1

Required:

  • -ScriptPath <string>: path to a .ps1 file.
  • -Version <version>: service version written to generated descriptor.

Optional:

  • -Name <string>: service name in generated descriptor. If omitted, defaults to script filename without extension.
  • -Description <string>: descriptor description. If omitted, defaults to Name.
  • -ServiceLogPath <string>: optional log path written to generated descriptor.
  • -PreservePaths <string[]>: optional relative files/folders preserved during service update --package.
  • -OutputPath <string>: output package path. If omitted, defaults to <Name>-<Version>.krpack in the current directory.
  • -Force: overwrite an existing output file.
  • -WhatIf and -Confirm: standard PowerShell safety switches.

Generated descriptor values in this mode:

  • FormatVersion = '1.0'
  • EntryPoint = '<script-file-name>.ps1'

Examples:

# Minimal ScriptPath mode (Name defaults from script filename)
New-KrServicePackage -ScriptPath .\Service.ps1 -Version 1.2.0

# Full metadata and preserve paths
New-KrServicePackage `
  -ScriptPath .\Service.ps1 `
  -Name 'my-service' `
  -Version 1.2.0 `
  -Description 'Production Kestrun service' `
  -ServiceLogPath '.\logs\service.log' `
  -PreservePaths @('config/production.json', 'data/', 'logs/') `
  -OutputPath .\my-service-1.2.0.krpack

Create a Package from a Single Script

If you only have one script file, package it directly. New-KrServicePackage will stage the script, generate Service.psd1, validate it, and produce a .krpack.

Minimal:

New-KrServicePackage -ScriptPath .\Service.ps1 -Version 1.2.0

Explicit metadata and output path:

New-KrServicePackage `
  -ScriptPath .\Service.ps1 `
  -Name 'my-service' `
  -Description 'Production Kestrun service' `
  -Version 1.2.0 `
  -OutputPath .\my-service-1.2.0.krpack

The generated descriptor in this mode always uses:

  • FormatVersion = '1.0'
  • EntryPoint = 'Service.ps1' (the script file name)

Optional: Generate a Checksum

For production safety, verify package integrity at install or update time.

Windows PowerShell:

Get-FileHash .\my-service.krpack -Algorithm SHA256 | Select-Object -ExpandProperty Hash

Linux/macOS shell:

sha256sum ./my-service.krpack

Keep the resulting hex hash for --content-root-checksum.

Install the Service (Package Input)

dotnet kestrun service install --package .\my-service.krpack

Use an explicit local runtime package for offline installs:

dotnet kestrun service install --package .\my-service.krpack --runtime-package .\Kestrun.Service.win-x64.1.0.0-rc.1.nupkg

--runtime-package also accepts a folder. Kestrun selects the expected Kestrun.Service.<rid>.<version>.nupkg file for the current platform and target version.

dotnet kestrun service install --package .\my-service.krpack --runtime-package .\artifacts\nuget --runtime-version 1.0.0-rc.1

Use a local/private runtime feed:

dotnet kestrun service install --package .\my-service.krpack --runtime-source .\artifacts\nuget --runtime-cache .\.kestrun-runtime-cache

Prefetch the service runtime into cache without installing a service bundle:

dotnet kestrun service install --runtime-version 1.0.0-rc.1 --runtime-source .\artifacts\nuget --runtime-cache .\.kestrun-runtime-cache

Use a direct runtime package URL with auth headers:

dotnet kestrun service install --package .\my-service.krpack --runtime-source https://packages.example.com/Kestrun.Service.win-x64.1.0.0-rc.1.nupkg --content-root-bearer-token <token>

service install requires a resolvable runtime package and does not fall back to the runtime payload bundled with Kestrun.Tool when runtime acquisition fails.

Runtime cache uses a canonical package path (packages/<id>/<version>/<id>.<version>.nupkg) and an extracted working payload path (expanded/<id>/...).

With checksum verification:

dotnet kestrun service install --package .\my-service.krpack --content-root-checksum <sha256-hex>

From an HTTP(S) URL:

dotnet kestrun service install --package https://downloads.example.com/my-service.krpack --content-root-checksum <sha256-hex>

From an HTTP(S) URL with bearer token auth:

dotnet kestrun service install --package https://downloads.example.com/my-service.krpack --content-root-bearer-token <token> --content-root-checksum <sha256-hex>

From an HTTP(S) URL with custom request headers:

dotnet kestrun service install --package https://downloads.example.com/my-service.krpack --content-root-header x-api-key:<key> --content-root-header x-env:prod --content-root-checksum <sha256-hex>

Ignore HTTPS certificate validation (insecure):

dotnet kestrun service install --package https://downloads.example.com/my-service.krpack --content-root-ignore-certificate --content-root-checksum <sha256-hex>

Choose Account and Bundle Location

Use --service-user when the service/daemon must run as a dedicated account.

dotnet kestrun service install --package .\my-service.krpack --service-user svc-kestrun

Use --deployment-root to control where bundles are staged.

dotnet kestrun service install --package .\my-service.krpack --deployment-root D:\KestrunServices

Start and Validate

dotnet kestrun service start --name my-service
dotnet kestrun service query --name my-service

Recommended checks:

  • Confirm service is running.
  • Confirm your API endpoints respond.
  • Confirm bootstrap logs and app logs are written to expected paths.

Upgrade Workflow (Service/Daemon)

  1. Build and publish a new .krpack payload (with incremented Version in Service.psd1).
  2. Stop the service.
  3. Run service update --package.
  4. Start service.
  5. Query service and API health.

Example:

dotnet kestrun service stop --name my-service
dotnet kestrun service update --name my-service --package .\my-service-v2.krpack --content-root-checksum <sha256-hex>
dotnet kestrun service start --name my-service
dotnet kestrun service query --name my-service

Optional module update (repository module only when newer than bundled):

dotnet kestrun service stop --name my-service
dotnet kestrun service update --name my-service --package .\my-service-v2.krpack --kestrun
dotnet kestrun service start --name my-service

Fail back to latest backup when needed:

dotnet kestrun service stop --name my-service
dotnet kestrun service update --name my-service --failback
dotnet kestrun service start --name my-service

Production Hardening Notes

  • Prefer .krpack deployments with checksum validation for repeatability.
  • Use least-privilege service accounts where possible.
  • Keep app configuration and secrets externalized where feasible.
  • Store service logs on durable storage and ship them to monitoring.
  • Validate restore/redeploy steps in a staging environment first.

Current Limits

  • service install --package and service update --package support local .krpack files and HTTP(S) package URLs.
  • service install --package resolves Kestrun.Service.<rid> for the current platform automatically. Use --runtime-package or --runtime-source to override runtime acquisition.
  • --runtime-package can point to a single .nupkg file or to a folder containing per-RID runtime packages.
  • service install can run without --package to prefetch the runtime cache when at least one runtime acquisition option is supplied.
  • Private package URLs and HTTP(S) runtime-source downloads can use bearer token auth via --content-root-bearer-token.
  • HTTPS certificate bypass is available via --content-root-ignore-certificate for package/runtime-source downloads and should be used only for controlled environments.

Return to the Guides index.