Perpetually polyglot: .NET on Platform.sh

Chad Carlson
Technical Writer
10 Oct 2019

At Platform.sh, we understand that modern web development isn’t restricted to just PHP or Node.js. It’s not a one language game out there; it’s a diverse ecosystem of languages and frameworks. We support eight different runtime languages out of the box, so that you have the tools you need to solve the problems those languages are best suited for.

This week, we’re excited to announce yet another runtime added to that lineup of supported languages: C#/.NET.

How would you deploy a .NET application on Platform.sh?

.NET applications need only slight modifications to run on Platform.sh. As a simple example, you can deploy a common “Getting Started” tutorial for the popular ASP.NET web framework. In this post, we’ll push the default ASP.NET MVC application built with the command dotnet new mvc -o PlatformAspNetCore described in that tutorial using the dotnet utility. You can find more detailed information about this process on the Platform.sh Community tutorial here.

Like any other application, .NET apps require at least three configuration files to run on Platform.sh. The first two files need to sit in a new .platform subdirectory of your project. You won’t need any services for this example, so you can create an empty services.yaml file in that subdirectory (it can be empty, but it must exist in your repository in order to deploy).

Then create a simple routes.yaml file there, too:

# .platform/routes.yaml

"https://www.{default}/":
    type: upstream
    upstream: "app:http"

"https://{default}/":
    type: redirect
    to: "https://www.{default}/"

The routes.yaml file defines how requests will be directed to our application, which will be named app, including redirects.

Next, you’ll need a .platform.app.yaml file in the project root, which tells Platform.sh how to handle builds, deploys, and what version of .NET Core will be used on Platform.sh.

# .platform.app.yaml

name: app

type: dotnet:2.2

disk: 512

hooks:
  build: |
    set -e
    dotnet publish --output "$PLATFORM_OUTPUT_DIR" -p:UseRazorBuildServer=false -p:UseSharedCompilation=false

web:
  commands:
    start: 'dotnet PlatformshAspNetCore.dll'

In this file, the application is named app, which matches the name in the routes.yaml file. It specifies the .NET Core version (2.2) and the amount of persistent disk given to the application (512 MB).

For simple applications like this, using the dotnet publish default framework-dependent deployment method in the build hook is sufficient to build in our .NET containers. Pass to that command the final output directory of the application (PLATFORM_OUTPUT_DIR, the environment variable for the general output directory for compiled languages at build time). Make sure to also pass the two additional flags that disable compilers which would normally start a collection of build servers, but are inadvisable in the Platform.sh build process.

The final start command (web.commands.start) launches the application using the dotnet utility directly, calling the built application PlatformshAspNetCore.

So this app is ready push to Platform.sh?

Almost. The entry point file Program.cs has to be modified slightly to receive a few environment variables defined in every Platform.sh project. Modify the Main method to pass the PORT and SOCKET environment variables as options to the Kestrel web server:

// Program.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;

namespace PlatformshAspNetCore
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = CreateWebHostBuilder(args);

            builder.UseKestrel(options =>
            {
                var port = Environment.GetEnvironmentVariable("PORT");
                if (port != null && Int16.TryParse(port, out var portNum))
                {
                  options.ListenLocalhost(portNum);
                }

                var socket = Environment.GetEnvironmentVariable("SOCKET");
                if (socket != null)
                {
                  options.ListenUnixSocket(socket);
                }
            });

            builder.Build().Run();
        }

        private static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args).UseStartup<Startup>();
    }
}

That’s it! Now the application is complete. Commit, push, and deploy!

There’s no reason to stop here, though. Any of our managed services can be configured for .NET applications. Want to use a MariaDB database? Create a DbContext, pass it credentials from the PLATFORM_RELATIONSHIPS environment variable, and add the database to your services.yaml file. There’s a Platform.sh template ready to show you how. Using a different version of .NET? Check the documentation, and see our supported versions.

Another language, ready to go on your polyglot PaaS!

Platform.sh .NET documentation

ASP.NET Core template

ASP.NET MVC Tutorial on Platform.sh Community