Scott Hanselman

ZEIT now deployments of open source ASP.NET Core web apps with Docker

March 16, 2017 Comment on this post [13] Posted in ASP.NET | Docker | DotNetCore
Sponsored By

ZEIT is a new cloud service and "now" is the name of their deployment tool. ZEIT World is their DNS service. If you head over to you'll see a somewhat cryptic animated gif that shows how almost impossibly simple it is to deploy a web app with ZEIT now.

ZEIT works with .NET Core and ASP.NET

You can make a folder, put an index.html (for example) in it and just run "now." You'll automatically get a website with an autogenerated name and it'll be live. It's probably the fastest and easiest deploy I've ever seen. Remember when Heroku (then Azure, then literally everyone) started using git for deployment? Clearly being able to type "now" and just get a web site on the public internet was the next step. (Next someone will make "up" which will then get replaced with just pressing ENTER on an empty line! ;) )

Jokes aside, now is clean and easy. I appreciate their organizational willpower to make an elegant and simple command line tool. I suspect it's harder than it looks to keep things simple.

All of their examples use JavaScript and node.js, but they also support Docker, which means they support open source ASP.NET Core on .NET Core! But do they know they do? ;) Let's find out.

And more importantly, how easy is it? Can I take a site from concept to production in minutes? Darn tootin' I can.

First, make a quick ASP.NET Core app. I'll use the MVC template with Bootstrap.

C:\Users\scott\zeitdotnet>dotnet new mvc
Content generation time: 419.5337 ms
The template "ASP.NET Core Web App" created successfully.

I'll do a quick dotnet restore to get the packages for my project.

C:\Users\scott\zeitdotnet>dotnet restore
Restoring packages for C:\Users\scott\zeitdotnet\zeitdotnet.csproj...
Generating MSBuild file C:\Users\scott\zeitdotnet\obj\zeitdotnet.csproj.nuget.g.props.
Generating MSBuild file C:\Users\scott\zeitdotnet\obj\zeitdotnet.csproj.nuget.g.targets.
Writing lock file to disk. Path: C:\Users\scott\zeitdotnet\obj\project.assets.json
Restore completed in 2.93 sec for C:\Users\scott\zeitdotnet\zeitdotnet.csproj.

NuGet Config files used:
C:\Program Files (x86)\NuGet\Config\Microsoft.VisualStudio.Offline.config

Feeds used:
C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\

Now I need to add a Dockerfile. I'll make one in the root that looks like this:

FROM microsoft/aspnetcore
LABEL name="zeitdotnet"
ENTRYPOINT ["dotnet", "zeitdotnet.dll"]
ARG source=.
COPY $source .

Note that I could have ZEIT build my app for me if I used the aspnetcore Dockerfile that includes the .NET Core SDK, but that would not only make my deployment longer, it would also make my docker images a LOT larger. I want to include JUST the .NET Core runtime in my image, so I'll build and publish locally.

ZEIT now is going to need to see my Dockerfile, and since I want my app to include the binaries (I don't want to ship my source in the Docker image up to ZEIT) I need to mark my Dockerfile as "Content" and make sure it's copied to the publish folder when my app is built and published.

  <None Remove="Dockerfile" />

  <Content Include="Dockerfile">

I'll add this my project's csproj file. If I was using Visual Studio, this is the same as right clicking on the Properties of the Dockerfile, setting it to Content and then "Always Copy to Output Directory."

Now I'll just build and publish to a folder with one command:

C:\Users\scott\zeitdotnet>dotnet publish
Microsoft (R) Build Engine version 15.1.548.43366
Copyright (C) Microsoft Corporation. All rights reserved.

zeitdotnet -> C:\Users\scott\zeitdotnet\bin\Debug\netcoreapp1.1\zeitdotnet.dll

And finally, from the .\bin\Debug\netcoreapp1.1\ folder I run "now." (Note that I've installed now and signed up for their service, of course.)

> Deploying ~\zeitdotnet\bin\Debug\netcoreapp1.1\publish
> Ready! (copied to clipboard) [3s]
> Upload [====================] 100% 0.0s
> Sync complete (196.18kB) [2s]
> Initializing…
> Building
> ▲ docker build
> ---> 035a0a1401c3
> Removing intermediate container 289b9e4ce5d9
> Step 6 : EXPOSE 80
> ---> Running in efb817308333
> ---> fbac2aaa3039
> Removing intermediate container efb817308333
> Step 7 : COPY $source .
> ---> ff009cfc48ea
> Removing intermediate container 8d650c1867cd
> Successfully built ff009cfc48ea
> ▲ Storing image
> ▲ Deploying image
> Deployment complete!

Now has put the generated URL in my clipboard (during deployment you'll get redirected to a lovely status page) and when it's deployed I can visit my live site. But, that URL is not what I want. I want to use a custom URL.

I can take one of my domains and set it up with ZEIT World's DNS but I like DNSimple (ref).

I can add my domain as an external one after adding a TXT record to my DNS to verify I own it. Then I setup a CNAME to point my subdomain to

C:\Users\scott\Desktop\zeitdotnet>now alias
> is a custom domain.
> Verifying the DNS settings for (see for help)
> Verification OK!
> Provisioning certificate for
> Success! Alias created: now points to [copied to clipboard]

And that's it. It even has a nice SSL certificate that they applied for me. It doesn't terminate to SSL all the way into the docker container's Kestral web server, but for most things that aren't banking it'll be just fine.

All in all, a lovely experience. Here's my Hello World ASP.NE Core app running in ZEIT and deployed with now  at (if you are visiting this long after this was published, this sample MAY be gone.)

I am still learning about this (this whole exercise was about 30 total minutes and asking Glenn Condron a docker question) so I'm not clear how this would work in a large multi-container deployment, but as long as your site is immutable (don't write to the container's local disk!) ZEIT says it will scale your single containers. Perhaps docker-compose support is coming?

Sponsor: Did you know VSTS can integrate closely with Octopus Deploy? Join Damian Brady and Brian A. Randell as they show you how to automate deployments from VSTS to Octopus Deploy, and demo the new VSTS Octopus Deploy dashboard widget. Register now!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
March 16, 2017 1:31
One of the coolest services I've seen in a while!!!

I signed up for a premium account, added a `Dockerfile` to my current NancyFX project I was in the middle of working on and simply ran the `now ...` commands.

After a few minutes of wrangling DNS over at cloudflare, we were all good and deployed!

The _ONLY_ issue I've encountered is that my base `/` route gives me a 500 error, but that is indicative of the `index.html` file missing.

Thanks for sharing this awesome utility/tool! I was looking for a better and more convenient "set it and forget it" type hosting for a few small services that will be lightly used. I don't like my options for deploying containers in Azure, though that was my "goto" choice at first. Seems like an awesome compromise :D.
March 16, 2017 2:03
Fixed my missing index.html issue by including "views" folder in copyToOutput! :)
March 16, 2017 3:20
Any particular reason for the "glenn" branding on the resulting site? Is it a security issue?
March 16, 2017 3:57
Mike C - Because Glenn Condron and I worked on it. ;)
March 16, 2017 12:39
This reminds me of a similar service from a couple of years ago called Looks like they aren't live anymore. But this is awesome cool! Pretty simple.
March 16, 2017 19:38
I've been working on something similar to this, although not very seriously, it was more for an NDC talk than anything else. Main difference is mine requires you to have set up a Docker Swarm somewhere.
March 16, 2017 19:57
This is cool @Scott, thought i'd follow the steps and try it out. Hit a hurdle in the deployment. Here's the console output

λ now
> Deploying ~\Source\Repos\zeitdotnet\bin\release\netcoreapp1.1
> Warning! Skipping file C:\Users\Matt\Source\Repos\zeitdotnet\bin\release\netcoreapp1.1\publish\refs\Microsoft.CodeAnalysis.dll (size exceeded 1MB)
> Warning! Skipping file C:\Users\Matt\Source\Repos\zeitdotnet\bin\release\netcoreapp1.1\publish\refs\Microsoft.CodeAnalysis.CSharp.dll (size exceeded 1MB)
> Warning! 2 of the files exceeded the limit for your plan.
> Please run `now upgrade` to upgrade.
> Ready! (copied to clipboard) [1s]
> Upload [====================] 100% 0.0s
> Sync complete (192.17kB) [2s]
> Initializing…
> Error! The build step of your project failed. To retry, run `now --force`.

tried both Debug and Release configs, but both include the CodeAnalysis assemblies which exceed the 1MB free plan limit. Did you use a paid account with your experiment, or found some other way around this?
March 16, 2017 22:00
I loved this so much I went and made an account and even ended up with a blog post of my own!

Hope this helps provide some more insight for some people:
March 17, 2017 3:16
Matt, yes I paid. I will ask them about that limit.
March 17, 2017 15:25
It's really great to see new startups developing more user friendly cloud hosting and deployments. I hope to see the big cloud providers follow suit and provide a much simpler 'beginners' cloud hosting solution without overwhelming the new user with thousands of options.

I looked into deploying a simple program into Azure recently as an API and was blocked by pay walls, different trial options, enter your credit card details, 'sign up for developer program' links, then after all that faff logging in and seeing a wall of options and icons...! Why is it so hard to just host a simple API?

I really think it's worth getting the hobbyist on board with new technologies as those are the people who turn up to work on Monday and talk excitedly about their side projects they hosted on the Sunday.
March 18, 2017 10:38
Hi Scott,

I have followed all the steps but end up with this:

D:\dotenetprojects\netcorernd\zeitditnet\bin\Debug\netcoreapp1.1>now --force
> Deploying D:\dotenetprojects\netcorernd\zeitditnet\bin\Debug\netcoreapp1.1
> Ready! (copied to clipboard) [3s]
> Upload [====================] 100% 0.0s
> Sync complete (140B) [2s]
> Initializing…
> Building
> ▲ docker build
> ---> 1e13808096de
> Removing intermediate container 6b7c20ebca7a
> Step 6 : EXPOSE 80
> ---> Running in 5b0aa02f1f0e
> ---> 9609b6963b21
> Removing intermediate container 5b0aa02f1f0e
> Step 7 : COPY $source .
> ---> 0b362ad4d724
> Removing intermediate container e7eb49288d2b
> Successfully built 0b362ad4d724
> ▲ Storing image
> ▲ Deploying image
> ▲ Container started
> Did you mean to run dotnet SDK commands? Please install dotnet SDK from:
> ▲ Deploying image
> ▲ Container started
> Did you mean to run dotnet SDK commands? Please install dotnet SDK from:
> ▲ Deploying image
> ▲ Container started
> Did you mean to run dotnet SDK commands? Please install dotnet SDK from:
> ▲ Deploying image
> ▲ Container started
> Did you mean to run dotnet SDK commands? Please install dotnet SDK from:

Is there anything i did wrong?

DockeFile Content:
FROM microsoft/aspnetcore
LABEL name="zeitdotnet"
ENTRYPOINT ["dotnet", "zeitdotnet.dll"]
ARG source=.
COPY $source .
March 29, 2017 11:59
What about database with this service ?
Thanks for sharing this tool !
March 30, 2017 0:09
To anybody who is reading this comment, this utility and service is amazing, use it you will see how great it is, thank you Scott.
تانک ازت

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.