Control lifetime of .NET Core console application hosted in docker
<p>Disclaimer - this is almost the same question as <a href="https://stackoverflow.com/q/38549006/685341">docker container exits immediately even with Console.ReadLine() in a .net core console application</a> - but I don't think accepted answer on this question is satisfactory.</p>

<p><strong>What I am trying to achieve</strong><br>
I am building a console application (it is a HTTP service using ServiceStack) which is built with .NET core (dnxcore50 - this is a console app, not an ASP.NET application). I am running this application in a docker container on a Linux machine. This I have done, and the HTTP service works.  </p>

<p><strong>My problem</strong><br>
Having said that 'my service works' - and it does, there is a problem hosting the service in a docker container. I am using <code>Console.ReadLine()</code> after starting up my HTTP listener, but this code does not block within the docker container and the container will exit immediately after starting. I can start the docker container in 'interactive' mode, and the service will sit there listening until I kill the interactive session and then the container will exit.   </p>

<p><strong>Code for Repo</strong><br>
The code below is a complete code listing for creating my test .NET core servicestack console application.  </p>

<pre><code>public class Program
{
    public static void Main(string[] args)
    {
        new AppHost().Init().Start("http://*:8088/");
        Console.WriteLine("listening on port 8088");
        Console.ReadLine();

    }
}

public class AppHost : AppSelfHostBase
{
    // Initializes your AppHost Instance, with the Service Name and assembly containing the Services
    public AppHost() : base("My Test Service", typeof(MyTestService).GetAssembly()) { }

    // Configure your AppHost with the necessary configuration and dependencies your App needs
    public override void Configure(Container container)
    {

    }
}

public class MyTestService: Service
{
    public TestResponse Any(TestRequest request)
    {
        string message = string.Format("Hello {0}", request.Name);
        Console.WriteLine(message);
        return new TestResponse {Message = message};
    }

}

[Api("Test method")]
[Route("/test/{Name}", "GET", Summary = "Get Message", Notes = "Gets a message incorporating the passed in name")]
public class TestRequest : IReturn&lt;TestResponse&gt;
{
    [ApiMember(Name = "Name", Description = "Your Name", ParameterType = "path", DataType = "string")]
    public string Name { get; set; }
}

public class TestResponse 
{
    [ApiMember(Name = "Message", Description = "A Message", ParameterType = "path", DataType = "string")]
    public string Message { get; set; }
}
</code></pre>

<p><strong>The old way of solving this problem</strong><br>
So having previously hosted using Mono (Mono had severe performance issues - hence the switch to .NET core) - the way to fix this behaviour was to use <code>Mono.Posix</code> listen for a kill signal like this:</p>

<pre><code>using Mono.Unix;
using Mono.Unix.Native;

...

static void Main(string[] args)
    {
        //Start your service here...

        // check if we're running on mono
        if (Type.GetType("Mono.Runtime") != null)
        {
            // on mono, processes will usually run as daemons - this allows you to listen
            // for termination signals (ctrl+c, shutdown, etc) and finalize correctly
            UnixSignal.WaitAny(new[] {
                new UnixSignal(Signum.SIGINT),
                new UnixSignal(Signum.SIGTERM),
                new UnixSignal(Signum.SIGQUIT),
                new UnixSignal(Signum.SIGHUP)
            });
        }
        else
        {
            Console.ReadLine();
        }
    }
</code></pre>

<p>Now - I understand that this won't work for .NET Core (obviously because Mono.Posix is for Mono!)  </p>

<p>The solution outlined in the related article (top of this post) is no use to me - in a production environment, I can't expect to keep a docker container alive by ensuring it has an interactive session available which will keep the Console.ReadLine working because there is a STD-IN stream there...  </p>

<p>Is there another way to keep my docker container alive (using the <code>-d</code> (detached) option when invoking <code>docker run</code>) when hosting a .NET Core application?  </p>

<p><strong>Code refactor as part of Mythz suggestion</strong>  </p>

<pre><code> public static void Main(string[] args)
    {
        Run(new AppHost().Init(), "http://*:8088/");
    }

    public static void Run(ServiceStackHost host, params string[] uris)
    {
        AppSelfHostBase appSelfHostBase = (AppSelfHostBase)host;

        using (IWebHost webHost = appSelfHostBase.ConfigureHost(new WebHostBuilder(), uris).Build())
        {
            ManualResetEventSlim done = new ManualResetEventSlim(false);
            using (CancellationTokenSource cts = new CancellationTokenSource())
            {
                Action shutdown = () =&gt;
                {
                    if (!cts.IsCancellationRequested)
                    {
                        Console.WriteLine("Application is shutting down...");
                        cts.Cancel();
                    }

                    done.Wait();
                };

                Console.CancelKeyPress += (sender, eventArgs) =&gt;
                {
                    shutdown();
                    // Don't terminate the process immediately, wait for the Main thread to exit gracefully.
                    eventArgs.Cancel = true;
                };

                Console.WriteLine("Application started. Press Ctrl+C to shut down.");
                webHost.Run(cts.Token);
                done.Set();
            }
        }
    }
</code></pre>

<p><strong>Final Solution!</strong>  </p>

<p>For Posterity - the solution I have gone with is the code which can be found here (Thanks to Myths for clarification): <a href="https://github.com/NetCoreApps/Hello/blob/master/src/SelfHost/Program.cs" rel="noreferrer">https://github.com/NetCoreApps/Hello/blob/master/src/SelfHost/Program.cs</a></p>

<p>Repo of the pertinent code:</p>

<pre><code>public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup&lt;Startup&gt;()
            .UseUrls("http://*:8088/")
            .Build();

        host.Run();
    }
}

public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // app.UseStaticFiles();

        app.UseServiceStack(new AppHost());

        app.Run(context =&gt;
        {
            context.Response.Redirect("/metadata");
            return Task.FromResult(0);
        });
    }
</code></pre>

<p>In NuGet, I have Microsoft.NETCore.App, ServiceStack.Core and ServiceStack.Kestrel installed.</p>