# Using .NET Aspire With the Docker Publisher

> A practical walkthrough of using .NET Aspire's Docker publisher to generate Docker Compose files from C# code. Learn how to set up an API with Postgres and Redis, publish to Docker Compose, and deploy to a VPS with minimal configuration.

Published: 2025-07-05. Author: Milan Jovanović.

Canonical: https://milanjovanovic.tech/blog/using-dotnet-aspire-with-the-docker-publisher

Aspire's Docker publisher, a preview package in mid 2025, turns the services you declare in C# into a `docker-compose.yml` and a matching `.env` file.
You call `AddDockerComposeEnvironment` in the AppHost, then run `aspire publish` to generate the artifacts.
Deploying them to a VPS is still your job.

[**.NET Aspire**](https://milanjovanovic.tech/blog/dotnet-aspire-a-game-changer-for-cloud-native-development) is one of the most exciting additions to the .NET ecosystem in years.

It brings a fresh, modern approach to building cloud-native apps, with a focus on developer productivity,
great defaults, and tight integration across your entire stack.

One of the most asked-for features is the ability to publish your app to Docker Compose.
This is now available in the latest preview, and I'm excited to share how it works.

In this post, I'll walk you through how I used Aspire's **Docker publisher** to spin up a demo app that includes an API, a Postgres database, and a Redis cache.
Everything runs using Docker Compose, and Aspire generates the whole thing from C# code.

I'll show you how to set it up, explain what it's doing behind the scenes,
and give you a glimpse into how easy it is to take that setup and run it on a VPS or cloud server.

## The Demo App

The app is intentionally simple, just enough to demonstrate how Aspire wires things together.

- API project: a [**minimal .NET Web API**](https://milanjovanovic.tech/blog/minimal-apis-dotnet)
- Postgres: used for data storage
- Redis: used for caching

In a traditional setup, you'd manually connect these services, manage configuration files, handle environment variables,
and write a `docker-compose.yml` from scratch.

With Aspire, you declare everything in C# inside the AppHost project.
Here's a quick look at the setup:

```csharp {4}
var builder = DistributedApplication.CreateBuilder(args);

// Enables Docker publisher
builder.AddDockerComposeEnvironment("aspire-docker-demo");

var postgres = builder.AddPostgres("database")
    .WithDataVolume();

var database = postgres.AddDatabase("demo-db");

var redis = builder.AddRedis("cache");

var webApi = builder.AddProject<Projects.Web_Api>("web-api")
    .WithReference(database).WaitFor(postgres)
    .WithReference(redis).WaitFor(redis);

builder.Build().Run();
```

This gives you a development environment where Aspire runs Postgres and Redis in containers, and connects your API to them.

The `AddDockerComposeEnvironment` method enables the Docker publisher.
It's available in the `Aspire.Hosting.Docker` NuGet package, which is currently in preview.

```powershell
Install-Package Aspire.Hosting.Docker -Version 9.3.1-preview.1.25305.6
```

## Installing the Aspire CLI

To publish your app to Docker Compose, install the **Aspire CLI**:

```bash
dotnet tool install --global aspire.cli --prerelease
```

Then you can run the `publish` command:

```bash
aspire publish -o docker-compose-artifacts
```

This command will scan your solution for the Aspire project and generate a Docker Compose file and an `.env` file
based on the services you've defined.

<div className="centered">
  ![Aspire publish command](https://milanjovanovic.tech/blogs/mnw_149/aspire_publish.png)
</div>

## The Docker Compose File

Let's examine what Aspire created for us:

```yml
services:
  database:
    image: 'docker.io/library/postgres:17.4'
    environment:
      POSTGRES_HOST_AUTH_METHOD: 'scram-sha-256'
      POSTGRES_INITDB_ARGS: '--auth-host=scram-sha-256 --auth-local=scram-sha-256'
      POSTGRES_USER: 'postgres'
      POSTGRES_PASSWORD: '${DATABASE_PASSWORD}'
    ports:
      - '8000:5432'
    volumes:
      - type: 'volume'
        target: '/var/lib/postgresql/data'
        source: 'aspire.apphost-1f0ed76b33-database-data'
        read_only: false
    networks:
      - 'aspire'
  redis:
    image: 'docker.io/library/redis:7.4'
    command:
      - '-c'
      - 'redis-server --requirepass $$REDIS_PASSWORD'
    entrypoint:
      - '/bin/sh'
    environment:
      REDIS_PASSWORD: '${REDIS_PASSWORD}'
    ports:
      - '8001:6379'
    networks:
      - 'aspire'
  web-api:
    image: '${WEB_API_IMAGE}'
    environment:
      OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EXCEPTION_LOG_ATTRIBUTES: 'true'
      OTEL_DOTNET_EXPERIMENTAL_OTLP_EMIT_EVENT_LOG_ATTRIBUTES: 'true'
      OTEL_DOTNET_EXPERIMENTAL_OTLP_RETRY: 'in_memory'
      ASPNETCORE_FORWARDEDHEADERS_ENABLED: 'true'
      HTTP_PORTS: '8002'
      ConnectionStrings__demo-db: 'Host=database;Port=5432;Username=postgres;Password=${DATABASE_PASSWORD};Database=demo-db'
      ConnectionStrings__redis: 'redis:6379,password=${REDIS_PASSWORD}'
    ports:
      - '8003:8002'
      - '8005:8004'
    depends_on:
      database:
        condition: 'service_started'
      redis:
        condition: 'service_started'
    networks:
      - 'aspire'
networks:
  aspire:
    driver: 'bridge'
volumes:
  aspire.apphost-1f0ed76b33-database-data:
    driver: 'local'
```

This `docker-compose` file is generated from the C# code we wrote earlier.
It defines the services, their images, environment variables, ports, and dependencies.

The `.env` file contains some of the configuration we need:

```txt
# Parameter database-password
DATABASE_PASSWORD=<YOUR_STRONG_PASSWORD>

# Container image name for web-api
# Change this to the imaage name in the container registry
WEB_API_IMAGE=web-api:latest

# Parameter redis-password
REDIS_PASSWORD=<YOUR_STRONG_PASSWORD>
```

It contains the passwords for the database and Redis, as well as the image name for the API.
You'll need to replace the placeholders with actual values.
For the API image, you can build and tag the image yourself, or use a pre-built one from a registry.

## Publishing and Running on a VPS

Aspire doesn't deploy the app for you, but it gives you everything you need.

Once the Compose file is ready, deployment to a VPS is straightforward:

1. Copy the artifacts to your server using (`scp` or `git`).
2. SSH into the VPS.
3. Run `docker compose up -d` inside the artifact directory.

Make sure [**Docker**](https://milanjovanovic.tech/blog/docker-dotnet-developers) and Docker Compose are installed on the server.

You can expose ports with a reverse proxy like Nginx or Caddy, and secure it with HTTPS using Let's Encrypt.

I'll cover this in more detail in a future post.

## Wrapping Up

.NET Aspire with Docker Compose provides a smooth developer experience and a simple way to deploy full-stack apps.

You define everything in C#, test it locally, and publish it with one command.
No need to write Compose files or manage infrastructure manually.
This opens up new possibilities for deploying and managing cloud-native apps.
You're not locked into a specific cloud provider, and you can easily move between environments.

Can I just say how much I love this?
I'm really excited about the future of cloud-native development with .NET and Aspire.

If you liked this article and want to learn how I structure larger apps, check out [**Modular Monolith Architecture**](https://milanjovanovic.tech/modular-monolith-architecture).
It walks you through building clean, scalable .NET applications with strong internal boundaries and maintainability in mind.

Thanks for reading and stay awesome!

---

## Frequently asked questions

### Can .NET Aspire generate a Docker Compose file?

Yes. In mid-2025 Aspire shipped a Docker publisher in the Aspire.Hosting.Docker NuGet package (then in preview). You call AddDockerComposeEnvironment in the AppHost project, and Aspire generates a complete docker-compose.yml and .env file from the services you declared in C#.

### How do you publish a .NET Aspire app to Docker Compose?

Install the Aspire CLI as a global dotnet tool (a prerelease at the time), then run aspire publish with an output directory. The command scans the solution for the AppHost and generates the Docker Compose file plus an .env file covering every service, port, environment variable, and dependency you defined.

### Does .NET Aspire deploy your application for you?

No. Aspire generates the deployment artifacts, and the rest is up to you. For a VPS: copy the artifacts over with scp or git, SSH in, and run docker compose up -d, then put a reverse proxy like Nginx or Caddy with HTTPS via Let's Encrypt in front.

### What does the Aspire-generated Compose file contain?

Everything the declared services need: pinned images for Postgres and Redis, environment variables, port mappings, a shared network, a data volume for the database, and depends_on ordering. The API service gets connection strings wired to the database and cache, with passwords pulled from the .env file.

### What is .NET Aspire?

Aspire is a .NET stack for building cloud-native apps with a focus on developer productivity, good defaults, and tight integration across your services. You declare your API, database, and cache in C# in an AppHost project, and Aspire runs the dependencies in containers and connects everything for local development.
