.NET and Java

Containers let you deploy applications built on frameworks such as .NET and Java without requiring users to install the .NET Framework or Java runtime.

Containerizing also assures that your application will run reliably regardless of what runtimes may already be installed on the endpoint.

Getting Started

We will begin by downloading the sample applications from GitHub. A nice trick to do this without installing Git is to create a temporary container with turbo try that is only used to clone the repository. The --mount=%CD%\samples flag is used to poke a hole in the container to the samples directory on the native file system so that the containerized Git application can write to it. (The %CD% special path variable represents the current directory.)

# Create a samples folder
> mkdir samples

# Download the sample applications from GitHub
> turbo try --mount=%CD%\samples git -- /c git clone


This example shows how to make an image for a .NET Framework application using TurboScript, an automated way to build application images.

The script copies the application binaries from the cloned repository to a new container and commits it to an image. The --mount=%CD%\samples flag is used to poke a hole in the container isolation so that the cloned repository can be accessed. The --no-base flag just marks microsoft/dotnet:4.5 as a dependency rather than copying it into the image. This greatly reduces the size of the final image.

# Build an image using the application binaries from the cloned repository

> turbo build --mount=%CD%\samples --name=dotNetApp --no-base

Below is the TurboScript used for this example project. You should copy and paste this code into an empty text file named

# Layer in the required version of the .NET Framework
layer microsoft/dotnet:4.5

# Create a new directory for your application
cmd mkdir C:\myApp

# Copy the application from the mounted path to the application folder
cmd copy %CD%\samples\dotnet-helloworld\DotNetHelloWorld.exe c:\myApp

# Set the startup file for the image to the application executable
startup file c:\myApp\DotNetHelloWorld.exe


Instead of using TurboScript automation, we will demonstrate a different and more interactive process for containerizing the Java sample application.

First, create a new container with the Java Development Kit, specified with the turbo new jdk:7 command. We will do all of our work in this container, such as copying and running the application. When everything is ready, the container state will be committed to an image.

The --mount=%CD%\samples flag is used to poke a hole in the container isolation so that the Java application can be copied from the host file system. The --detach flag specifies that the turbo command should not wait for the container to finish.

# Start a new Java Development Kit container
> turbo new jdk:7 --detach --mount=%CD%\samples

# Create a folder for the Java application
(3df234f3) > mkdir C:\javaApp

# Copy the Java application from the host file system to the container
(3df234f3) > xcopy /s samples\java-simple-webserver C:\javaApp

# Set the working directory to C:\javaApp
(3df234f3) > pushd C:\javaApp

# Run the server from within the container
(3df234f3) C:\javaApp> "%java_home%\bin\java.exe" -jar SimpleWebServer.jar

The Java application is now running on your local machine using port 80, which can be confirmed by opening a web browser to http://localhost. You should see a listing of all the files in the C:\javaApp folder of the container.

Now that the Java application has been copied to the container and is running correctly, we can proceed to the final step of committing the container state to an image. To do that, shutdown the container using the turbo stop command with the container identifier.

# Stop the container
> turbo stop 3df234ff3

Commit the container state to an image using the turbo commit command.

# Commit the container
> turbo commit 3df234f3 simple-java-webserver

By default, committing will generate a final image that also contains the base oracle/jdk:7 dependency. This may be desirable for portability if you plan on exporting the image for use with Turbo Studio or Server, or if the base dependency may get deleted later on. However, it will also increase the size of the image. Instead, you may want to soft-link the oracle/jdk:7 dependency by using the --no-base flag. A soft-linked dependency will be pulled automatically and added to the container by our layering mechanism on startup.

# Commit with a soft-linked dependency
> turbo commit --no-base 3df234f3 simple-java-webserver

Creating releases

Each time an image is pushed a new version is generated. But usually only certain versions are released to end users. These images can be marked as releases using the turbo release command.

To illustrate this idea, we’ll create a 1.0 release from our newly created image.

# Create a new release version 1.0 for the image
> turbo release simple-java-webserver 1.0

When turbo images is executed, you should now see a new entry for simple-java-webserver:1.0.

> turbo images
ID            Name                   Release   Created                Size
--            ----                   -------   -------                ----
7a85fe8f7ad1  oracle/jdk             7.65      8/22/2014 11:34:19 AM  74.3 MB
9iejrk2a34hd  git/git                1.94      8/21/2014 11:32:00 AM  50.4 MB
3a24fade3ea4  simple-java-webserver            8/22/2014 11:52:32 AM  20.2 MB
3a24fade3ea4  simple-java-webserver  1.0       8/22/2014 11:59:59 AM  20.2 MB

Deploying to the Hub

Now that we have built our image, we can deploy it to end users by pushing it to the Turbo Hub.

> turbo push simple-java-webserver

This will create a new repository in your account called simple-java-webserver where the image will be placed.

Note that if you tagged your image in the previous section, use the command:

>turbo push simple-java-webserver:1.0.

Web Applications

Turbo can also be used to containerize web applications. Let’s see how to use Turbo with two popular web frameworks — ASP.NET and Node.

Creating an ASP.NET application container

# Create a new container with .NET, ASP.NET, and git
> turbo run microsoft/dotnet,microsoft/aspnet --using=git/git

Downloading dotnet from
Downloading aspnet from
Downloading git from
Running container 249c4f3e with visibility private

Inside the container session, use the git clone command to move the application into the container.

(249c4f3e) > cd c:\
(249c4f3e) > git clone

We use IIS Express to launch the ASP.NET application:

# Start the ASP.NET application console
(249c4f3e) > start "MiniBlog" "C:\Program Files (x86)\IIS Express\iisexpress.exe" /path:C:\samples\aspnet-MiniBlog\Website

We can see the ASP.NET application start up as the IIS output is logged in the new console window.

Copied template config file 'C:\Program Files (x86)\IIS Express\AppServer\applicationhost.config' to 'C:\Users\Administrator\appdata\local\temp\iisexpress\applicationhost2014112420457848.config'
Updated configuration file 'C:\Users\Administrator\appdata\local\temp\iisexpress\applicationhost2014112420457848.config' with given cmd line info.
Starting IIS Express ...
Successfully registered URL "http://localhost:8080/" for site "Development Web Site" application "/"
Registration completed
IIS Express is running.
Enter 'Q' to stop IIS Express

Let’s confirm the application is running by opening http://localhost:8080 in a browser.

Login using username demo and password demo.

And finally create a new post!

Creating a Node.js container

In this example we’ll containerize aIRChat, an open source IRC client built on the popular Node.js framework.

Start a new container using the turbo run command specifying the Node.js image to be included and applying as before a transient Git layer:

> turbo run node --using=git

In the container, first create a folder where we can clone the aIRChat project from GitHub.

> mkdir projects
> cd projects

Use the git clone command again to copy the application contents into the container.

> git clone
Cloning into 'airchat'...
remote: Reusing existing pack: 2983, done.
remote: Counting objects: 21, done.
remote: Compressing objects: 100% (18/18), done.
Receiving objects:  99% (remote: Total 3004 (delta 9), reused 0 (delta 0)
Receiving objects: 100% (3004/3004), 8.84MiB | 948.00 KiB/s, done.
Resolving deltas: 100% (1302/1302), done.
Checking connectivity... done.

Once the application files are present, we can use the standard npm package manager to install the aIRChat application:

> cd .\airchat\Content
> npm install

We are now ready to launch the application!

# Launch a Node.js server
> node app.js

connect.multipart() will be removed in connect 3.0
visit for alternatives
connect.limit() will be removed in connect 3.0
Express server listening on port 3000

aIRChat is now running on port 3000. You can confirm this by opening a browser to http://localhost:3000. Once you’ve verified that aIRChat is running, stop the Node.js server by entering Ctrl+C. At the command prompt type exit to shut down the container session. When you’re finished configuring the container, committing it will create a new merged image.

# Specify a name for the new image
> turbo commit 1be755fcfafc4cf0b8e1c0667f6d13f0 aIRChat

Servers supports both desktop and server applications. For the most part, server application images behave in the same way as desktop application images. However, special configuration considerations often arise with server applications.

Multiple database server versions

Turbo can be used to run database servers such as SQL Server, MySQL, ElasticSearch, MongoDB, and others.

In this example, we will use Turbo to run multiple versions of the free SQL Server Express database and access them from the database management client.

> turbo run sqlserver/sqlserver2012-express -d --route-block=tcp,udp -n=sql1
> turbo run sqlserver/sqlserver2014-express -d --route-block=tcp,udp -n=sql2
> turbo run sqlserver/ssms2012 -d --link=sql1:sql1 --link=sql2:sql2

Note that we have used container linking and naming to connect the SSMS management client to the two database instances, and route blocking to prevent the database ports from being accessible on the host device’s network.

Login using the username sa and password password1 to the database servers sql1 and sql2. You can confirm the two databases are manageable through the client and are two distinct versions.

Multiple instances of a single server

In this example, we’ll use TCP port mapping, a special case of network routing, to run multiple instances of the Ghost blogging engine on the same host.

By default, Ghost uses port 2368 to listen to requests. Since only one server can listen on a given port at a time, we need to remap this port to allow multiple Ghost instances to run simultaneously:

# Create a container that maps port 8080 on the host to port 2368 on the container.
C:\> turbo new -d --route-add=tcp://2368:8080 ghost

# Create a container that maps port 8081 on the host to port 2368 on the container.
C:\> turbo new -d --route-add=tcp://2368:8081 ghost

# Create a container that maps port 8082 on the host to port 2368 on the container.
C:\> turbo new -d --route-add=tcp://2368:8082 ghost

The tcp:// prefix indicates that the routing rule should apply to TCP traffic. The rule 2368:8080 causes the container’s port 2368 to be mapped to the host device port 8080. We can then launch a second and third instance by mapping 2368 to host port 8081 and 8082.

You can check that three distinct Ghost instances are accessible by browsing to localhost:8080, localhost:8081, and localhost:8082. You can also test what happens if you try to launch instances without applying the routing rules!

Factoring a database into a layer

A useful practice when constructing container architectures is to use layering to factor data from the underlying server application.

For example, suppose that we have a Ghost blog that’s been populated with some data. We would like to move the data into a separate layer. This will allow us to layer in just the database contents on top of any compatible base Ghost image. Saving the database in a distinct layer can help you test different application versions against the same data, back up database contents, or quickly rollback a database to a fixed base state during development.

First, we need to create a blank container that will hold the database:

# Create a clean container
> turbo new clean --no-run

We do not want to actually run the new container so we used the --no-run flag.

Next, use the turbo cp command to copy the database from the Ghost container (bc53e584 in our example) to the blank container:

> turbo cp bc53e584:c:\ghost\content\data d65260ad:c:\ghost\content\data

Note the d65260ad: prefix used for the target folder. This tells the copy command to place the contents into a particular folder. If no container prefix is used, the host device is assumed. The c:\ghost\content\data folder is the location for the database contents in this example.

Now that the database container is ready, we commit it into a new image called ghost-db:

# Commit the container to a new image
> turbo commit d65260ad ghost-db

Finally, we can layer ghost-db into any base Ghost server container and to pre-populate it with our content:

# Create a ghost container with the ghost-db content layered on top of it
> turbo run ghost,ghost-db

Talk to us.