The .NET SDK generates container images that run in a rootless configuration by default - this means that the user the application runs on doesn't have root permissions. This has a number of implications, but broadly means that rootless applications cannot perform actions like
- write files to their local application directory
- bind to ports lower than 1024
- insert more here
Let's take a look at how that might appear for an application that stores data in a local sqlite database.
Let's publish our console application using the SDK to our local Docker engine:
$ dotnet publish -t:PublishContainer
MSBuild version 17.8.3+195e7f5a3 for .NET
Determining projects to restore...
All projects are up-to-date for restore.
super-sql-app -> C:\Users\chethusk\Code\container-workshop\super-sql-app\bin\Debug\net7.0\linux-x64\super-sql-app.dll
super-sql-app -> C:\Users\chethusk\Code\container-workshop\super-sql-app\bin\Debug\net7.0\linux-x64\publish\
Building image 'super-sql-app' with tags 'latest' on top of base image 'mcr.microsoft.com/dotnet/runtime:7.0'.
Pushed image 'super-sql-app:latest' to local registry via 'docker'.
Our app is targeting .NET 7 currently. This application is a very simple storage app that either
- writes a given name to a sqlite database, or
- retrieves the id of a name from the sqlite database
Sqlite writes data to a local database, which in our app is called hello.db
. Let's run our new app:
$ docker run -it --rm super-sql-app set Chet
Inserted Chet as 1
Great, now lets update our app to target .NET 8 by updating the TargetFramework property
$ grep TargetFramework super-sql-app.csproj
TargetFramework>net8.0</TargetFramework>
Now publish (note that the .NET 8 base images are used):
> dotnet publish -t:PublishContainer
MSBuild version 17.8.3+195e7f5a3 for .NET
Determining projects to restore...
All projects are up-to-date for restore.
super-sql-app -> C:\Users\chethusk\Code\container-workshop\super-sql-app\bin\Release\net8.0\super-sql-app.dll
super-sql-app -> C:\Users\chethusk\Code\container-workshop\super-sql-app\bin\Release\net8.0\publish\
Building image 'super-sql-app' with tags 'latest' on top of base image 'mcr.microsoft.com/dotnet/runtime:8.0'.
Pushed image 'super-sql-app:latest' to local registry via 'docker'.
and run:
$ docker run -it --rm super-sql-app set Chet
Unhandled exception. Microsoft.Data.Sqlite.SqliteException (0x80004005): SQLite Error 14: 'unable to open database file'.
at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
at Microsoft.Data.Sqlite.SqliteConnectionInternal..ctor(SqliteConnectionStringBuilder connectionOptions, SqliteConnectionPool pool)
at Microsoft.Data.Sqlite.SqliteConnectionPool.GetConnection()
at Microsoft.Data.Sqlite.SqliteConnectionFactory.GetConnection(SqliteConnection outerConnection)
at Microsoft.Data.Sqlite.SqliteConnection.Open()
at Program.<>c__DisplayClass0_0.<<Main>$>g__EnsureTable|2() in C:\Users\chethusk\Code\container-workshop\super-sql-app\Program.cs:line 76
at Program.<>c__DisplayClass0_2.<<Main>$>b__4() in C:\Users\chethusk\Code\container-workshop\super-sql-app\Program.cs:line 46
at Program.<Main>$(String[] args) in C:\Users\chethusk\Code\container-workshop\super-sql-app\Program.cs:line 10
What happened?! In .NET 8 our container is running as nonroot and our application tries to create a sqlite database file in the application directory. At this point we have a few options
$ docker run -it --rm --user root super-sql-app set Chet
Inserted Chet as 1
This works, but requires you to change your deployment.
Note - this is currently bugged in .NET SDK 8.0.100 - follow this issue for details.
By setting ContainerUser
to root, you can tell the SDK explicitly to operate in a root-capable mode. You can set this via the command line or project properties:
$ dotnet publish -t:PublishContainer -p ContainerUser=root
MSBuild version 17.8.3+195e7f5a3 for .NET
Determining projects to restore...
All projects are up-to-date for restore.
super-sql-app -> C:\Users\chethusk\Code\container-workshop\super-sql-app\bin\Release\net8.0\super-sql-app.dll
super-sql-app -> C:\Users\chethusk\Code\container-workshop\super-sql-app\bin\Release\net8.0\publish\
Building image 'super-sql-app' with tags 'latest' on top of base image 'mcr.microsoft.com/dotnet/runtime:8.0'.
Pushed image 'super-sql-app:latest' to local registry via 'docker'.
Now run the container and you should see the application successfully write to the database:
$ docker run -it --rm super-sql-app set Chet
Inserted Chet as 1
In this application the database is being written to the local app directory. A more safe thing to do might be to write the database to the user's home directory instead:
var dataSource = "/home/app/hello.db";
Now, publish the app:
$ dotnet publish -t:PublishContainer
MSBuild version 17.8.3+195e7f5a3 for .NET
Determining projects to restore...
All projects are up-to-date for restore.
super-sql-app -> C:\Users\chethusk\Code\container-workshop\super-sql-app\bin\Release\net8.0\super-sql-app.dll
super-sql-app -> C:\Users\chethusk\Code\container-workshop\super-sql-app\bin\Release\net8.0\publish\
Building image 'super-sql-app' with tags 'latest' on top of base image 'mcr.microsoft.com/dotnet/runtime:8.0'.
Pushed image 'super-sql-app:latest' to local registry via 'docker'.
and run it one final time:
$ docker run -it --rm super-sql-app set Chet
Inserted Chet as 1