dotnet-deploy
April 22, 2026 ยท View on GitHub
dotnet-deploy is a .NET tool for publishing a project to a remote Linux server over SSH and optionally managing the matching systemd service.
It is built for the common case of deploying a single ASP.NET Core or console application to a host you control:
- discover the target
.csproj - build a self-contained Linux publish output for the remote CPU architecture
- upload and extract the package on the server
- restart the matching
systemdservice when it already exists - generate and install a
systemdunit file when needed
Features
- SSH deployment to remote Linux hosts
- Password or private-key authentication
- Automatic runtime identifier selection from the remote machine architecture
- Optional extra file inclusion into the publish output
- Deployment settings from
appsettings.json,appsettings.deploy.json, and User Secrets - Multi-host deployment through
Deploy:Hosts - Built-in
systemdinstall, uninstall, restart, and status commands
Requirements
Local machine:
- .NET SDK 8 or later
- A deployable .NET project
Remote machine:
- A Linux host reachable over SSH
- A user that can run
sudo systemdif you want service management- A supported CPU architecture:
x86_64->linux-x64aarch64->linux-arm64
Installation
Global installation
dotnet tool install -g dotnet-deploy
Run the global tool with:
dotnet-deploy -h
Local installation
dotnet new tool-manifest
dotnet tool install dotnet-deploy
Run the local tool with:
dotnet tool run dotnet-deploy -h
Quick Start
Publish to a remote host
dotnet-deploy publish --host 192.168.1.10 --username root --password abc123
During publish, the tool:
- discovers the project file
- loads deployment settings
- connects to the remote server over SSH
- runs a self-contained local
dotnet publish - compresses the publish output
- uploads the archive to the remote host
- extracts files into the remote application directory
- attempts to restart the matching
systemdservice if it already exists
Install as a systemd service
dotnet-deploy systemd install --host 192.168.1.10 --username root --password abc123
Check service status
dotnet-deploy systemd status --host 192.168.1.10 --username root --password abc123
Project Discovery
By default, the tool starts from the current working directory.
Accepted --project values:
- a
.csprojfile path - a directory that contains exactly one
.csproj - a relative or absolute path
If the directory contains multiple project files, pass the exact .csproj path.
Examples:
dotnet-deploy publish --project ./src/MyApp/MyApp.csproj --host 192.168.1.10
dotnet-deploy publish --project ./src/MyApp --host 192.168.1.10
Configuration
The tool loads deployment configuration from the project directory in this order:
appsettings.jsonappsettings.deploy.json- User Secrets for the target project, when
UserSecretsIdis defined
All deployment settings are read from the Deploy section.
Minimal configuration
{
"Deploy": {
"Host": "192.168.1.10",
"Username": "root"
}
}
Store the password in User Secrets:
dotnet user-secrets set Deploy:Password abc123
Then publish with:
dotnet-deploy publish
Example appsettings.deploy.json
{
"Deploy": {
"Host": "192.168.1.10",
"Username": "root",
"BeforeCommand": "dotnet test",
"AfterCommand": "echo publish completed",
"IncludeFiles": [
"appsettings.Production.json",
"Assets/*"
],
"Systemd": {
"Unit": {
"Description": "MyApp service"
},
"Service": {
"RestartSec": 3,
"Environment": {
"ASPNETCORE_URLS": "http://0.0.0.0:8080",
"ConnectionStrings__Default": "..."
}
}
},
"Hosts": {
"staging": {
"Host": "192.168.1.20",
"Username": "deploy",
"Systemd": {
"Service": {
"Environment": {
"ASPNETCORE_ENVIRONMENT": "Staging"
}
}
}
},
"production": {
"Host": "192.168.1.30",
"Username": "deploy"
}
}
}
}
Supported Deploy properties
Top-level settings:
HostUsernamePasswordPrivateKeyIncludeFilesBeforeCommandAfterCommandSystemdHosts
Entries under Deploy:Hosts:<name> override the shared top-level settings for that host.
Authentication
The tool supports:
- password authentication via
Password - private key authentication via
PrivateKey
When a private key is used, Password can also act as the private key passphrase.
Examples:
dotnet-deploy publish --host 192.168.1.10 --username root --password abc123
dotnet-deploy publish --host 192.168.1.10 --username root --private-key ~/.ssh/id_rsa
Multi-Host Deployment
If Deploy:Hosts is configured, you can select a named host directly:
dotnet-deploy publish --host production
Or deploy to all configured hosts in sequence:
dotnet-deploy publish --all-hosts --host staging
Behavior notes:
- the host selected by
--hostorDeploy:Hostruns first --all-hoststhen runs the same command for every configured host inDeploy:Hosts- the initial host is skipped if it appears again in
Deploy:Hosts
Publish Behavior
Local build output
publish creates temporary output under:
<project-directory>/bin/__dotnet_deploy_temp__/publish
It also creates a temporary archive at:
<project-directory>/bin/__dotnet_deploy_temp__/publish.tar.gz
Remote deployment paths
The remote root directory is:
/opt/dotnet
For a project whose assembly name is MyApp, deployment uses:
- application directory:
/opt/dotnet/MyApp - uploaded archive:
/opt/dotnet/MyApp.tar.gz
IncludeFiles
IncludeFiles entries must be relative paths.
The tool will:
- copy an explicitly named file if it exists
- search the project directory recursively for matching patterns and copy matches into the publish output
Absolute paths are rejected.
BeforeCommand and AfterCommand
If BeforeCommand or AfterCommand is configured, that command runs locally in the project root directory.
Typical uses:
- run tests before publishing
- build additional assets
- run a post-publish script
systemd Integration
The tool can generate a systemd service based on the project assembly name.
Default generated values include:
WorkingDirectory=/opt/dotnet/<AssemblyName>ExecStart=/opt/dotnet/<AssemblyName>/<AssemblyName>Restart=alwaysRestartSec=10KillSignal=SIGINTSyslogIdentifier=<AssemblyName>Environment=ASPNETCORE_ENVIRONMENT=ProductionWantedBy=multi-user.target
During systemd install, the tool also adds:
User=<ssh username>
The generated service file is uploaded to:
/opt/dotnet/<AssemblyName>.service
Then it is linked into:
/etc/systemd/system/<AssemblyName>.service
Override generated systemd content
Use Deploy:Systemd to override or extend the generated unit.
Example:
{
"Deploy": {
"Systemd": {
"Unit": {
"Description": "My custom service"
},
"Service": {
"RestartSec": 5,
"Environment": {
"ASPNETCORE_ENVIRONMENT": "Production",
"MyOptions__Enabled": "true"
}
},
"Install": {
"WantedBy": "multi-user.target"
}
}
}
}
Commands
publish
Publish the project to a remote host.
dotnet-deploy publish [options]
Common options:
--host--username--password--private-key--project--all-hosts--include-files
info
Display resolved deployment information for the target project and host.
dotnet-deploy info [options]
This command prints:
- assembly name
- project root directory
- temporary working directory
- resolved
.csprojpath - selected host
- resolved deployment options as JSON
- generated
systemdservice content
systemd install
Generate, upload, enable, and start a systemd service.
dotnet-deploy systemd install [options]
systemd uninstall
Stop and disable the service, then remove the uploaded service file.
dotnet-deploy systemd uninstall [options]
systemd restart
Restart the service.
dotnet-deploy systemd restart [options]
systemd status
Show service status using systemctl status --no-pager -l.
dotnet-deploy systemd status [options]
Examples
Deploy using only configuration
dotnet-deploy publish
Deploy a specific project file
dotnet-deploy publish --project ./src/MyApp/MyApp.csproj --host production
Deploy with additional files
dotnet-deploy publish --host production --include-files appsettings.Production.json --include-files wwwroot/*
Install and start the service
dotnet-deploy systemd install --host production
Check resolved settings before deploying
dotnet-deploy info --host production
Troubleshooting
The project directory contains multiple .csproj files
Pass the exact project file:
dotnet-deploy publish --project ./src/MyApp/MyApp.csproj
The tool says the host is empty
Provide --host explicitly or set Deploy:Host in configuration.
The service is not restarted after publish
publish attempts to stop and restart <AssemblyName> automatically, but only if the service already exists on the remote machine. Install it first:
dotnet-deploy systemd install --host production
The server architecture is not supported
At the moment, only these remote architectures are mapped for publish:
x86_64aarch64
Help
Use -h or --help on any command.
Examples:
dotnet-deploy -h
dotnet-deploy publish -h
dotnet-deploy systemd -h
dotnet-deploy systemd install -h