Creating Images

In the previous section, we used several ready-to-use images from the Hub. But what if we want to create or customize our own images?

Customizing applications

Recall that in the previous section we launched several Firefox containers from the Hub. Applications in the Hub are configured with installer defaults. Let’s see how to customize images with our own preferred settings.

Launch a new Firefox container as before:

> turbo new firefox

In this example, we modify the Firefox container by setting the homepage to a new URL from the Options menu:

Close the browser window to stop the container.

We suggest restarting the container to verify the all of the settings are configured as desired. This will be the same experience as your users when they launch the final image:

> turbo start e4a84281
Using VM 11.8.960 from local
Using image vcredist:2008 from local
Using image clean:25 from local
Using image firefox:47.0.1 from local
Using image firefox-base:47.0.1 from local
Running existing container firefox#e4a84281 with visibility private

Notice that if we use the new command to start a new Firefox container, the default home page still appears.

> turbo new firefox
Using VM 11.8.960 from local
Using image vcredist:2008 from local
Using image clean:25 from local
Using image firefox:47.0.1 from local
Using image firefox-base:47.0.1 from local
Running new container firefox#b05176b2 with visibility private

To publish our modified Firefox container, we must create a new image. To create an image from a container, use the turbo commit command:

> turbo commit e4a8 firefox-turbo

The first argument to commit is the identifier of the container being committed. Observe again that we used prefix matching to shorten the container identifier. The second argument is the name of the new image. By convention, modifications to a base image are named following the pattern -.

Finally, we can launch a new instance of our custom image!

> turbo new firefox-turbo

Notice that a new container identifier has been assigned. Even though the configuration is the same as our initial container e4a84281, the container we just launched is a new instance of a container instantiated off the firefox-turbo image.

Removing images

Since we only created the firefox-turbo image for testing purposes, we’d like to remove it to keep our local repository clean.

Images can be removed using the rmi (remove image) command:

> turbo rmi turbo-clean
Image firefox-turbo was removed

The rmi command also accepts wildcards. For example, all images can be removed from a device with the command turbo rmi *.

Installing custom applications

So far we have been using applications that were already available in the Hub. But what if we want to install completely custom applications into our containers?

In this example, we’ll install Sublime Text editor into a container. (Sublime Text is a popular text editor.) To follow along with this demo, you’ll need to download the Sublime Text installer:

Download Sublime Text 2.0.2a Setup.exe

We assume in this example that the installer is downloaded to the folder C:\Installers.

You can install applications into containers just like you would a normal desktop. However notice we have a small problem — how do we get the installer binary into the container?

The --mount flag causes the specified directory to be mounted within the container. Mounted directories on the host device are accessible from within the container context exactly as they would be on the host device.

# Mount a host folder into the container that contains the setup file 
> turbo new clean --mount="C:\Installers"

# The native C:\Installers folder is now accessible from the container command prompt
(clean#94d6338f) C:\>cd "C:\Installers"
(clean#94d6338f) C:\Installers>dir
 Volume in drive C has no label.
 Volume Serial Number is DADA-BCA1

 Directory of C:\Installers

07/01/2016 01:58 PM <DIR> .
07/01/2016 01:58 PM <DIR> ..
07/01/2016 01:58 PM 5,601,488 Sublime Text 2.0.2a Setup.exe
 1 File(s) 5,601,488 bytes
 2 Dir(s) 114,467,921,920 bytes free

Notice that unlike a normal clean image, this container has a C:\Installers directory visible that contains the Sublime Text setup file.

Next, launch the setup and click through the installer as you would normally. Once it’s complete, you can navigate to the C:\Program Files (x86)\Sublime Text 2 folder to confirm that Sublime Text has been installed in the container:

# Run the installer to install the program into the container
(clean#94d6338f) C:\Installers>"Sublime Text 2.0.2a Setup.exe"

# Verify the install
(clean#94d6338f) C:\>cd "Program Files (x86)\Sublime Text 2"
(clean#94d6338f) C:\Program Files (x86)\Sublime Text 2>dir
 Volume in drive C has no label.
 Volume Serial Number is DADA-BCA1

 Directory of C:\Program Files (x86)\Sublime Text 2

07/01/2016 02:01 PM <DIR> .
07/01/2016 02:01 PM <DIR> ..
09/15/2010 10:06 PM 71,680    bz2.pyd
09/15/2010 10:06 PM 1,852     Microsoft.VC90.CRT.manifest
09/15/2010 10:06 PM 572,928   msvcp90.dll
09/15/2010 10:06 PM 653,136   msvcr90.dll
07/31/2012 09:54 PM 4,206
07/01/2016 02:01 PM <DIR>     Pristine Packages
09/15/2010 10:06 PM 153,088   pyexpat.pyd
09/15/2010 10:06 PM 2,145,280 python26.dll
09/15/2010 10:06 PM 1,484,587
09/15/2010 10:06 PM 11,776    select.pyd
03/10/2012 08:23 AM 10,838
07/08/2013 11:25 AM 3,921,408 sublime_text.exe
09/15/2010 10:06 PM 585,728   unicodedata.pyd
07/01/2016 02:01 PM 13,381    unins000.dat
07/01/2016 02:01 PM 1,179,960 unins000.exe
07/01/2016 02:01 PM 20,903    unins000.msg
09/15/2010 10:06 PM 73,728    _ctypes.pyd
09/15/2010 10:06 PM 86,016    _elementtree.pyd
09/15/2010 10:06 PM 286,208   _hashlib.pyd
09/15/2010 10:06 PM 40,448    _socket.pyd
09/15/2010 10:06 PM 665,600   _ssl.pyd
 20 File(s) 11,982,751 bytes
 3 Dir(s) 114,456,604,672 bytes free

Finally we can launch the sublime_text.exe executable!

Take note of the full path to the startup file as we’ll need it later.

# Run the program inside of the container
(clean#94d6338f) C:\Program Files (x86)\Sublime Text 2>sublime_text.exe

Saving new images

Now we have a useful container that has Sublime Text installed.

As before, we’ll use the commit command to create an image that we can distribute. But in this case we do not want the command prompt to open by default. An alternate startup file can be specified at commit time using the --startup-file flag.

Here we commit the container to a new image using the startup file path we noted previously:

# Save changes to the container to an image.
# The startup-file flag allows the image to automatically run an executable instead of opening a command prompt.
> turbo commit 94d6338f sublimetext --startup-file="C:\Program Files (x86)\Sublime Text 2\sublime_text.exe"
Committing container clean#94d6338f to image sublimetext
Commit complete

Now launch a fresh sublimetext container using the image we just created:

# Run the new image, the run command is similar to the new command but instead runs an existing container of the same image name if they exist
> turbo run sublimetext

Desktop Integration

Sometimes we want containers to behave as isolated environments. Other times, we want the container to interact (in a managed way) with the host desktop.

Turbo lets you to control the level and types of integration with the host desktop.

Accessing local files

You may have noticed in previous examples that you could not access the My Documents or My Downloads file when using containerized applications. This is because the container has its own instance of the filesystem by default. In many cases, we may want to allow access to these built-in folders.

Turbo allows you to selectively de-isolate individual folders between the container and host device. We saw an example of this earlier with the --mount flag. But explicitly mounting all of the user folders would be tedious. Also, user folder names vary between operating system variants.

To solve this, Turbo provides a built-in isolation mode that de-isolates all major user folders:

> turbo run notepadplusplus --isolate=full+merge-user

When Notepad++ starts, notice that you can view and edit files in your user folders.

Shell integration

Full desktop integration extends beyond just the ability to access the host filesystem. For example, we may want the application’s Start Menu shortcuts, file associations, and other shell integrations to work as if the application was natively installed.

Turbo lets you “install” images via the installi (install image) command. Installed images are “wired up” to the shell and behave as if they are installed on the host device. But note that these applications are not actually installed — they are still running in the Turbo container environment, just with shell interactions and different isolation settings.

To install an image, we use the command:

> turbo installi 7-zip
Shortcut 7-zip created in Start Menu

>turbo installi notepadplusplus
Shortcut notepadplusplus created in Start Menu

If you press the Windows key, you will notice that 7-Zip and Notepad++ appear in the Start Menu! You can launch these applications through the normal Windows application launch experience.

Also, notice that if you right-click on a file in the Windows Explorer, you see the shell extensions normally associated with installed versions of 7-Zip and Notepad++:

Installing a container does not automatically cause the image contents to download. The contents will download automatically when the application is first run. Container installation is intentionally designed this way so that many applications can be registered on the desktop very quickly.

To remove the applications, use the uninstalli command:

> turbo uninstalli 7-zip
Shortcut for 7-zip removed from Start Menu

> turbo uninstalli notepadplusplus
Shortcut for notepadplusplus removed from Start Menu

You can check that the Start Menu shortcuts, file associations, and shell extensions have been removed.

Installed containers can also be removed through the Add/Remove Programs section of the Control Panel.


Turbo containers have a virtual networking stack that can be customized on a per-container basis. You can also connect containers with one another and with the host device’s network adapters.

Site whitelisting

The route-block and route-add flags control which domains and protocols are allowed to interact with the container.

A nice application of container networking is to block all IP addresses except specified websites in a browser.

> turbo new firefox --route-block=ip --route-add=ip://

This will launch a new Firefox browser with all IP addresses blocked except for the IP address associated with To verify this, try navigating to a a few websites and notice that only Yahoo’s site is accessible!

Routing rules are applied in left-to-right order. The first rule blocks all IP traffic. Subsequent rules can then add back specific routes. In our example, we could have added additional --route-add arguments to allow access to additional web sites. The ip:// prefix indicates that all IP traffic, including both TCP and UDP protocols, should be affected by the routing rule.

Virtual networks

Suppose we want to run multiple containers on an isolated virtual network such that the containers can communicate with one another.

In this example, we’ll create a WordPress server and then access it with a Firefox web browser.

# Launch a WordPress server in a virtual network environment
> turbo run wordpress -d --network=wp --name=web 

# Run a Firefox browser instance in the same virtual network
# environment and connect to the WordPress server
> turbo run --network=wp firefox http://web:8080

The --network flag creates a named virtual network. Containers started in the same virtual network can communicate with one another. The --name flag assigns a friendly name to the container which is also used to reference the container via virtual DNS resolution.

Notice that the Firefox browser is able to connect to the WordPress server at the http://web:8080 address. You may also confirm that the WordPress server is not externally accessible on the device.

Container linking

It is also possible to connect individual containers together via container linking.

We can repeat the example above using the --link command:

# Launch a WordPress server in a virtual network environment
> turbo run -d --network=wp --name=web wordpress

# It is also possible to map a different domain name than
# the container name by setting up a container-to-container link
> turbo run firefox

Container linking provides more fine grained control over container interactions but may be more complicated to use than named virtual networks.

This example also demonstrates the use of container name remapping. In this case, we mapped Firefox to the web container as before but modified its DNS entry to

Network layering

Network configurations can be stored in images and layered just like any other image. This is very useful when applying the same set of routing rules to multiple images. The Hub also provides pre-configured network routing images.

For example, the following command creates a Firefox browser with social media websites blocked:

> turbo run firefox,block-social-routes

The block-social-routes is a layer provided by the Hub that has route blocking rules defined for popular social media sites.

Notice that the Firefox browser will not navigate to sites like and


When operating in a developer environment, the recommended practice is to automate creation of Turbo images.

Turbo supports a flexible scripting language called TurboScript. In this section, we’ll see how to use TurboScript in conjunction with different external tools to automate container builds.

Building images with TurboScript

In this example, we’ll use TurboScript to automatically build VLC Media Player, a popular media player for Windows.

To execute a TurboScript build, we use the turbo build command. The script reference can be either a local path or a URL.

> turbo build

We have provided the build script for this application at that URL. (More on this later.) Notice that by convention the TurboScript name is

If you have run this script before, you should add the --overwrite flag to the command to allow the build to overwrite the previously created image.

After a couple minutes, a new vlc image should be created. Try running it to confirm it works!

> turbo run vlc

Inside TurboScript

Let’s look at what this script does. It starts by defining the namespace, name and title of the container image that will be created.

meta title="VLC stable"
meta namespace="videolan"
meta name="vlc"

Next we want to fetch the VLC homepage, parse the contents and extract the download link and version number for the latest release. The tools we chose for the job are wget and Python. Both of these tools are available in the Hub.

We bring these into the script’s scope as transient dependencies. Transient layers are available while the script is executing but are automatically removed when the final image is saved. Transient layers are applied with the using command.

# we'll use wget to download files and python to fetch and parse VLC homepage
using gnu/wget,python/python:3.4.1
# we'll need the 'requests' python module
cmd pip install requests --quiet

The cmd verb executes the given command. Here we use the Python pip package manager to install additional components.

With our tools ready we parse the download URL from the homepage and fetch the VLC setup file:

 echo import requests >>
 echo import re >>
 echo host = "" >>
 echo headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0'} >>
 echo r = requests.get(host , headers=headers, timeout=10) >>
 echo url = ''.join(list(re.findall('(\/vlc\/.*exe)', r.text)[0])) >>
 echo print(url) >>

cmd python
 var url = last

# Download
 cmd "wget --no-check-certificate --no-verbose -O VlcSetup.exe %url%"

Notice another great TurboScript trick here — the batch block! The batch verb lets us inline script files within the script itself, avoiding the need to pull in an external .py file and allowing us to keep our script self contained. Scopes in TurboScript are determined by whitespace alignment, similar to Python.

Also notice the last keyword. This special variable is substituted by TurboScript with the return value from the previous command. Here it is used to capture the output from the Python script and place it into a TurboScript variable.

The next step is to install VLC. When using automation, be sure to use silent install options (the /S flag in this case).

# Install
 cmd VlcSetup.exe /S

With VLC installed the container image is ready. We do some final Python scripting to parse out the version number and set that as the container version tag:

 echo import requests >>
 echo import re >>
 echo host = "" >>
 echo headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:41.0) Gecko/20100101 Firefox/41.0'} >>
 echo r = requests.get(host , headers=headers, timeout=10) >>
 echo version = ''.join(list(re.findall('(\/vlc\/(.*)\/win.*exe)', r.text)[0][1])) >>
 echo print(version) >>

cmd python
 var version = last

We were only able to cover some TurboScript basics here. To learn more, view the TurboScript Reference Guide.

Scripting builds

Now that we have our TurboScript and a command to run the build, we need to automate the rest of the build process.

Add the follow commands to your automated CI build script:

# Log in to your account
turbo login <username> <password>

# Execute the TurboScript and build a new image
turbo build -n=<name> C:\path\to\

# Push the image to our repo
turbo push <name>

(You may want to use an API key instead of your password for automated logins. We'll discuss API keys in just a moment.)

Once the image is pushed, other users and processes can pull and run it instantly!

Integrating with MSBuild and Visual Studio

Now let's integrate our container build step with MSBuild, Visual Studio's build system. (The same basic steps will work with any IDE.)

The easiest way to integrate with Visual Studio/MSBuild is to add a Post-build event to your project.

Right-click on your project in Visual Studio and select Build Events from the left-hand menu.

In the Post-build event command line box, add the line:

turbo build -n=$(SolutionName) $(SolutionDir)\

For solutions with multiple projects, we recommend only triggering a post-build event for the last project in the build chain. This may require customizing your TurboScript to also pull in the build outputs from these other projects.

As before, you could also optionally add a turbo push event to automatically generate a repository version on each build. This is the recommended practice for continuous integration style development.

Automating with Jenkins

Jenkins is a popular and free open source continuous integration (CI) system. Turbo directly integration with Jenkins that allows container images to be built as part of the CI process.

To follow along with this section, you'll need to install the TurboScript plugin for Jenkins into your Jenkins environment. Install it by opening the Jenkins Plugin Manager and install the latest TurboScript plugin.

First we need to setup the Jenkins job. Save the script file on the Jenkins host machine. Change line #15 to use your own username.

From the Jenkins Dashboard create a new Jenkins job of type Turbo Project. Add an Execute TurboScript build step and connect it to the script just saved.

We want to push the container image created to the Hub, so we add a Push Turbo Image build step:

Save the new Jenkins job and trigger a run to see your automated build in action!

Automating Turbo with Chocolatey

Chocolatey is a package manager for Windows desktop applications. We can use TurboScript to leverage Chocolatey to create Turbo images. This is powerful since any application with a Chocolatey package can be used to create a Turbo container. As of this writing there are nearly 3,000 distinct Chocolatey packages available.

Let's repeat our VLC example using Chocolatey instead of a native TurboScript:

> turbo new --name=choco-vlc clean --using=chocolatey/chocolatey

This command creates a new container named choco-vlc. The --using flag lets applies Chocolatey as a transient layer within the container. Recall that this will allow us to use Chocolatey within the container but remove it from the final image. We saw this earlier in the TurboScript but here we see how to apply using from the command line.

In some cases other dependencies may be required to install the chocolate package such as PowerShell. In this case, just add additional transient layers: --using=chocolatey/chocolatey,microsoft/powershell.

In the command window that opens run:

choco install vlc -y

The VLC package should start to download and install successfully within the container.

When the install has completed, close the command window.

> exit

In the original command window used to create the container, we commit our image.

> turbo commit choco-vlc my-vlc --startup-file="@PROGRAMFILESX86@\VideoLAN\VLC\vlc.exe"

Note we set the appropriate startup file using the --startup-file flag so the container runs VLC on startup (rather than a command prompt containing VLC).

We can now test the image by launching it in a new container. In a command window, enter:

> turbo new my-vlc

VLC should launch:

Sample TurboScripts on GitHub

TurboScripts for many popular applications have been open sourced and are available in the turboapps GitHub repository.

Looking at examples is a quick way to pick up the basics of TurboScript. Pull these down, customize your builds -- and remember to contribute your own TurboScripts back to the community by sending a pull request!

API keys for scripting and authentication

To allow authentication in the context of scripting and automation, Turbo supports authentication via API keys. API keys eliminate the need to hard code passwords or pass them as parameters to your scripts.

The first step is to create an API key. To do this, go to your organization settings, and then select Devices & API Keys on the right menu. (You can also generate and manage API keys for an individual account on the Account Settings page.)

Once here, click Add API Key to generate a new key.

Notice that when you hover your mouse over an API key a Remove button is shown. Use this button to remove the key and revoke its access to your organization.

There is no limit to the number of keys you can generate. You may decide to create and manage different keys for different teams or for different user access, allowing you the flexibility to revoke them independently without disrupting all key users.

Click Copy Key and head back to the command line.

Using the API key is easy -- just include the key in the turbo login command using the --api-key flag:

> turbo login --api-key=9ZoKH_336g0MqP2yptwfrv9B1XUm8YFPnCZNugVQNr4
testorg logged in at 2/26/2016 3:53:31 PM

The session is now authenticated in the organization account context. The account will stay logged in until the API key is revoked or the session is closed with the turbo logout command.

Turbo on Citrix and RDS

System administrators can use Turbo to quickly deploy applications and custom containerized environments on Citrix, Parallels RAS, Workspot, Windows Remote Desktop Services (RDS), and other remote desktop systems.


To get started, you will need to download and install Turbo for Windows for all users on the system.

If users will be streaming the application window rather than the whole desktop, we recommend hiding the Turbo GUI using the --hide-gui flag.

> start /wait turbo-client.exe --all-users --silent --hide-gui

Adding Applications to the Start Menu

Turbo application desktop integration, including Start Menu, desktop shortcuts, and file associations, can be enabled with the turbo installi command along with the --all-users flag. If you omit the version from the application identifier, then the latest version will be used.

# Sign in to (sign up at
> turbo login 

# Add the latest Firefox ESR browser to the Start Menu
> turbo installi --all-users mozilla/firefox-esr

# Add the latest Google Chrome browser to the Start Menu
> turbo installi --all-users google/chrome

# Add Internet Explorer 8 with Java 6.45 to the Start Menu
> turbo installi --all-users microsoft/ie:8,jre:6.45

The Firefox ESR, Chrome,and IE8 with Java6 applications will appear in the Start Menu of the server.

Adding Applications to the Delivery Platform

Depending on the delivery platform in use, you may also need to add the applications in the delivery system’s administration interface.

In this example, we will add applications to Citrix XenApp using Citrix Studio.

Start Citrix Studio and go to Applications > Add Applications.

Select the option to Add applications From Start Menu

Then select the Firefox ESR, Chrome, and IE8 with Java6 applications.

Click through to Finish to complete the process.

Running the Applications

Once added to Citrix Studio users can access the applications from the Citrix StoreFront website or the Citrix Receiver application.

Click on an application to launch it.

Note that because applications are executed in isolated Turbo container environments, any combination of browsers, plugins, and runtimes can run side-by-side on the same server.

For example, an Internet Explorer 8 with Java 6 environment can run alongside an Internet Explorer 10 with Java 7 environment or another Internet Explorer 8 with Java 7 environment:

This allows elimination of Citrix siloing and consolidation of servers to a single version of Citrix using a single base image.

Advanced Topics has many capabilities that we didn’t discuss in other sections. Here are a few:

Synchronizing state across machines optionally supports state synchronization. This allows application state to be saved and continue automatically on a different device.

To enable state synchronization, launch the container with the --enable-sync flag:

# Begin work in container on device A
> turbo run --enable-sync python
(python#a37b20f9) C:\> exit

Saving state of container python#a37b20f9
Container python#a37b20f9 stopped in state cc525b1b
 `start python#a37b20f9` to restart execution locally
 `continue cc525b1b` to continue execution remotely

Notice that when state synchronization is enabled, a state identifier is generated when the container is stopped. The state identifier includes information about the state of the container as it existed upon termination and is used to identify a point at which continuation of execution should occur on another device. In this example, the state identifier is cc525b1b.

If you have another device available, login and use the command:

# Continue execution on device B
> turbo continue cc525b1b
Continuing execution of container python#a37b20f9 in state cc525b1b
(python#a37b20f9) C:\>

(You should use the state identifier you received when you closed your version of the container.) uses efficient differential synchronization techniques that only transmit information on changes across devices. The entire container does not need to be copied across devices if the other machine already has some of the container information. This makes it much faster to migrate state across devices.

Importing third party package formats supports importing of several application package formats directly into Turbo container images. This is useful if investment has already been made in packaging applications in a third party package format.

In this example, we import a virtual application package from ThinApp into a Turbo containers:

# Import the ThinApp package described by package.ini into the appname image
> turbo import -n=appname thinapp C:\thinapp-project\package.ini

Container Skinning

When many container instances are running it can be difficult to distinguish between applications, particularly if they are subtle variants of a single base application. Skinning makes it easy to visually distinguish between container contexts.

Turbo currently supports a skinning driver that surrounds visible container windows with a specified color:

# Identify different Java versions by window skin
> turbo run jre:8.0,firefox+skin(green)
> turbo run jre:7.51,firefox+skin(red)
> turbo run jre:6.45,firefox+skin(0x0000ff)

Organization Dashboard

To access the dashboard in an administrator mode, you need to sign in to your account on the page. After accessing your personal dashboard, click on the profile icon in the upper right corner and switch to your organization's dashboard:

You can now search for new applications and add them to the dashboard (or remove the ones that are already there):

Being an admin of the organization, allows you to also publish images under the organization namespace. After adding a new image, it will appear on the dashboard, but the only way to run it would be using the “My Machine” mode. To enable custom applications to run in the cloud please contact

Running Applications Using a Shareable URL

Turbo allows organization administrators to create a URL for end users to run applications without having to log into the dashboard.

First create an API key under the organization. Log into your organization's administrator or owner account and navigate to your organizations Devices & API Keys page. The Devices & API Keys page can be found through the Profile and Account link on your organization's dashboard, or directly through the URL{org-name}/settings/devices-and-api-keys. Click Add API Key to create an API key which will be embed in the Turbo URL as a query parameter. Give it a name you can remember (and possibly revoke later). Copy this value to your text editor.

Next, we will obtain a URL to launch and application under the organizations namespace. Navigate to your organization's dashboard (click on your profile dropdown). The organization's dashboard should be at the url{org-name}. Right click on the application you would like to run using an anonymous url. Select Share Application. A new dialog will open, with the option to select the API key from the previous step. The Launch Location dropdown allows the URL to specify whether the application will launch directly in the HTML5 client, or attempt to launch using the native client via the Turbo Protocol. Copy the Share URL to a text editor. It should look something like{app-uuid}&user={org-name}&title={app-title}&loc=remote.

To test your link, open an incognito browser window with without logging in to and paste the URL into the browser address bar. The applciation should run without requiring a login. Please be aware that the share URLs contain an API Key that allows access to your organization's applications and should be kept secure within the organization.

Questions? Projects? Talk to us.