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
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
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.