This Quickstart guide will show you how to use Docker Compose to set up and run a .NET Core/PostgreSQL app. Before starting, you’ll need to have Compose installed.
Start by setting up the three files you’ll need to build the app. First, since your app is going to run inside a Docker container containing all of its dependencies, you’ll need to define exactly what needs to be included in the container. This is done using a file called Dockerfile
. To begin with, the Dockerfile consists of:
FROM microsoft/dotnet
RUN mkdir /myapp
WORKDIR /myapp
ADD . /myapp
That’ll put your application code inside an image that will build a container with .NET Core and all your dependencies inside it. For more information on how to write Dockerfiles, see the Docker user guide and the Dockerfile reference.
docker-compose.yml
is where the magic happens. This file describes the services that comprise your app (a database and a web app), how to get each one’s Docker image (the database just runs on a pre-made PostgreSQL image, and the web app is built from the current directory), and the configuration needed to link them together and expose the web app’s port.
version: '2'
services:
db:
image: postgres
web:
build: .
command: dotnet run
volumes:
- .:/myapp
ports:
- "5000:5000"
depends_on:
- db
environment:
ASPNETCORE_ENVIRONMENT: Development
ASPNETCORE_URLS: http://*:5000
With those three files in place, you can now generate the .NET Core skeleton app using docker-compose run
:
$ docker-compose run web dotnet new -t Web
Update the Dockerfile to include project.json
, because the dotnet new
command does not have the ability to force generate. Including this step will cache any packages that have been restored.
FROM microsoft/dotnet
RUN mkdir /myapp
WORKDIR /myapp
ADD project.json /myapp/project.json
ADD project.lock.json /myapp/project.lock.json
RUN dotnet restore
ADD . /myapp
First, Compose will build the image for the web
service using the Dockerfile
. Then it’ll run dotnet new
inside a new container, using that image. Once it’s done, you should have generated a fresh app:
$ ls -l
total 56
total 88
drwxr-xr-x 5 user staff 170 Sep 9 13:07 Controllers
drwxr-xr-x 4 user staff 136 Sep 9 13:07 Data
-rw-r--r-- 1 user staff 68 Sep 9 13:06 Dockerfile
drwxr-xr-x 5 user staff 170 Sep 9 13:07 Models
-rwxr--r-- 1 user staff 550 Jun 21 20:31 Program.cs
-rwxr--r-- 1 user staff 2190 Jun 21 20:31 README.md
drwxr-xr-x 5 user staff 170 Sep 9 13:07 Services
-rwxr--r-- 1 user staff 3105 Jun 21 20:31 Startup.cs
drwxr-xr-x 8 user staff 272 Sep 9 13:07 Views
-rwxr--r-- 1 user staff 252 Jun 21 20:31 appsettings.json
-rwxr--r-- 1 user staff 204 Jun 21 20:31 bower.json
-rw-r--r-- 1 user staff 338 Sep 9 13:07 docker-compose.yml
-rwxr--r-- 1 user staff 1148 Jun 21 20:31 gulpfile.js
-rwxr--r-- 1 user staff 227 Jun 21 20:31 package.json
-rwxr--r-- 1 user staff 3211 Jun 21 20:31 project.json
-rwxr--r-- 1 user staff 549 Jun 21 20:31 web.config
drwxr-xr-x 6 user staff 204 Sep 9 13:07 wwwroot
If you are running Docker on Linux, the files dotnet new
created are owned by root. This happens because the container runs as the root user. Change the ownership of the the new files.
sudo chown -R $USER:$USER .
If you are running Docker on Mac or Windows, you should already have ownership of all files, including those generated by dotnet new
. List the files just to verify this.
In order to connect the application to PostgreSQL a couple more changes will be necessary.
Update project.json
to include references to PostgreSQL's .NET Core packages:
"dependencies": {
...
"Npgsql.EntityFrameworkCore.PostgreSQL": "1.0.1",
"Npgsql.EntityFrameworkCore.PostgreSQL.Design": "1.0.1"
...
}
Update Startup.cs
to change the database connection to use PostgreSQL instead of SQLite:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
...
}
Create project.lock.json
as a simple json file:
{}
Now that you’ve got a new project.json
, you need to build the image again. (This, and changes to the Dockerfile itself, should be the only times you’ll need to rebuild.)
$ docker-compose build
To ensure that the lock file gets updated with restored packages run dotnet restore
:
$ docker-compose run web dotnet restore
The app is now bootable, but you’re not quite there yet. By default, .NET Core expects a database to be running on localhost
- so you need to point it at the db
container instead. You also need to change the database and username to align with the defaults set by the postgres
image.
Create a new configuration file appsettings.Development.json
with the following:
{
"ConnectionStrings": {
"DefaultConnection": "User ID=postgres;Host=db;Database=postgres"
}
}
You can now boot the app with:
$ docker-compose up
If all’s well, you should see some PostgreSQL output, and then—after a few seconds—the familiar refrain:
web_1 | Project myapp (.NETCoreApp,Version=v1.0) will be compiled because expected outputs are missing
web_1 | Compiling myapp for .NETCoreApp,Version=v1.0
web_1 |
web_1 | Compilation succeeded.
web_1 | 0 Warning(s)
web_1 | 0 Error(s)
web_1 |
web_1 | Time elapsed 00:00:04.6780194
web_1 |
web_1 |
web_1 | info: Microsoft.Extensions.DependencyInjection.DataProtectionServices[0]
web_1 | User profile is available. Using '/root/.aspnet/DataProtection-Keys' as key repository; keys will not be encrypted at rest.
web_1 | Hosting environment: Development
web_1 | Content root path: /myapp
web_1 | Now listening on: http://*:5000
web_1 | Application started. Press Ctrl+C to shut down.
Finally, you need to create the database. In another terminal, run:
$ docker-compose run web dotnet ef database update
That’s it. Your app should now be running on port 5000 on your Docker daemon. If you’re using Docker Machine, then docker-machine ip MACHINE_VM
returns the Docker host IP address.