Professor Sloth

Free web performance master class

Learn about web performance and how to make your site faster, delivered straight to your inbox.

Episode 9: Running ASP.NET Core Applications Using Systemd and Ansible

A web application isn’t much use if it isn’t running. We’ll hand in our neckbeard cards and copy+paste our way out of the problem. Last episode, we configured NGINX to act as a reverse proxy for our ASP.NET Core application. We tested our setup by manually running the .NET Core app from an SSH session. Today we’ll fix that stop-gap step by running our application as a service with systemd.

Create a New Systemd Service with Ansible

We need a few tasks to add a new systemd service. Code deploys will happen later once the server is configured by Ansible. This will cause our service to fail on startup until the first deploy is done. This chicken and egg situation is addressed with some conditionals in the Ansible playbook.


---
- hosts: dotnetHosts

  tasks:
    # Configure application directories so they are ready for deploy
    - name: ensure permissions are correct and app folder is present
      file:
        path: /var/www/aspnetCoreApp
        owner: root
        group: root
        state: directory

    - name: ensure the deploy directory exists
      file:
        path: /var/www/aspnetCoreApp/app
        owner: root
        group: root
        state: directory

    # Copy new service and set it to start automatically
    - name: copy service unit file for aspnetCoreApp
      copy: src=files/aspnetCoreApp.service dest=/etc/systemd/system/aspnetCoreApp.service
      notify: reload app service

    - name: check for installed service
      stat: path=/var/www/aspnetCoreApp/app/aspnetCoreAppExecutable
      register: installedFile

    - name: ensure service is running (When app is installed)
      service: name=aspnetCoreApp state=started enabled=yes
      when: installedFile.stat.exists

    - name: ensure service is stopped to avoid errors (When app is NOT installed)
      service: name=aspnetCoreApp state=stopped enabled=yes
      when: installedFile.stat.exists == False


  handlers:
    # Tell systemd to re-read the service unit files to find our new service
    - name: reload app service
      systemd: daemon_reload=yes
systemd-dotnet-playbook.yml

Run ASP.NET Core as a Systemd Service

Each service managed by systemd has its own “unit” file which describes how the service should work. Our service file was largely copied from the Microsoft .NET Core with NGINX documentation.

NOTE: We run our application as root in the service file. We shouldn’t do this but did anyway to keep moving forward. Using root fixed file permission errors caused during startup of our single-file app. This is a known issue in .NET Core 3.1 which will be addressed in a service release.


[Unit]
Description=aspnetCoreApp

[Service]
WorkingDirectory=/var/www/aspnetCoreApp/app
ExecStart=/var/www/aspnetCoreApp/app/aspnetCoreAppExecutable
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=aspnetCoreApp
# We need to fix this! The app should run under www-data.
User=root
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
Environment=DOTNET_CLI_TELEMETRY_OPTOUT=true

[Install]
WantedBy=multi-user.target
files/aspnetCoreApp.service -> /etc/systemd/system/aspnetCoreApp.service

The application is now properly hosted on our Linux server. NGINX will front requests from the public internet and systemd will ensure our application stays running.

Next we’ll install Redis using a pre-built role from Ansible Galaxy. Also, our Ansible playbook needs some cleanup to keep things maintainable.

Jordan Griffin
VP Engineering Request Metrics