You have written your application. You have tested it. You have copied the files to a server. Now what?
Your app needs to run. Not just once. It needs to run all the time. It needs to start again if the server reboots. It needs to restart if it crashes.
Many people reach for simple tools they know. They might run the process in a screen
session or use nohup
. These feel easy at first but they are brittle. What happens when the process dies? Who is monitoring it? How do you view its logs?
There is a better way. It is built into almost every modern Linux distribution. It is called systemd. It looks intimidating but for a developer you only need to know a few simple concepts to solve this problem correctly.
Systemd is the first process that starts when a modern Linux system boots. Its process ID is 1. It is the parent of all other processes. Its job is to start stop and manage everything else. This includes system services like the network or SSH daemon. It can also include your application.
You tell systemd what to do using simple text files called unit files. For running your app the most common type is a service file. This single file can replace a whole folder of custom shell scripts.
Let’s say you have a Python web application that you start with Gunicorn. You want systemd to manage it. You will create a file at /etc/systemd/system/myapp.service
. The name is important but the .service
suffix is the key part.
It looks like this.
[Unit]
Description=My Python Application
After=network.target
[Service]
User=deploy
Group=deploy
WorkingDirectory=/home/deploy/myapp
ExecStart=/home/deploy/venv/bin/gunicorn --workers 3 --bind unix:/home/deploy/myapp/myapp.sock myapp.wsgi:application
Restart=always
[Install]
WantedBy=multi-user.target
This might look complex but it is simple. Let’s break it down.
The [Unit]
section describes the service and its dependencies.
Description
is a human readable name that shows up in status commands.
After=network.target
tells systemd to wait until the network is ready before starting your app. This is usually what you want for a web service.
The [Service]
section defines how to run your app.
User
and Group
specify that the process should not run as root. This is a critical security practice. You should always create a dedicated user for your application.
WorkingDirectory
is the directory where the command will be executed from.
ExecStart
is the most important part. It is the exact command to start your application. Notice we use the full path to the gunicorn
executable inside our virtual environment. This is important because systemd’s PATH
is not the same as your interactive shell.
Restart=always
is the magic. It tells systemd to automatically restart your application if it ever exits for any reason unless it is explicitly stopped.
The [Install]
section tells systemd when the service should be started during boot.
WantedBy=multi-user.target
means your service should start when the system enters the standard multi user mode which is the normal state for a server.
Once you have saved your myapp.service
file you need to tell systemd about it. Systemd reads unit files into memory at boot so it does not see new files automatically.
First you reload the configuration.
# Tell systemd to read the new or changed file
sudo systemctl daemon-reload
Now you can start your service.
# Start the service now
sudo systemctl start myapp.service
You should check its status to make sure it started correctly.
# Check the status
sudo systemctl status myapp.service
The output will tell you if it is active and running. It will also show the last few lines of its log output which is useful for debugging.
To make your application start automatically when the server boots you enable the service.
# Enable the service to start on boot
sudo systemctl enable myapp.service
This creates a symbolic link that systemd uses during the boot sequence. It only needs to be done once.
Systemd also provides a complete logging system called the journal. It captures all the standard output and standard error streams from your service automatically. You can view logs with journalctl
.
# View all logs for your service
sudo journalctl -u myapp.service
# Follow the logs in real time
sudo journalctl -u myapp.service -f
# See the last 100 lines
sudo journalctl -u myapp.service -n 100
This is much better than trying to manage your own log files. The journal is structured so you can filter by time or severity. It handles log rotation for you. You get all this for free just by using a service file.
You should never hardcode secrets like database URLs or API keys in your service file. Anyone with read access to the file could see them. A better way is to put them in a separate file and tell systemd where to find it.
First create an environment file at /home/deploy/myapp/.env
.
DATABASE_URL=postgres://user:password@localhost/dbname
SECRET_KEY=yoursecretkeygoeshere
Make sure this file is not readable by other users on the system.
sudo chown deploy:deploy /home/deploy/myapp/.env
sudo chmod 600 /home/deploy/myapp/.env
Now update your service file to use it.
[Service]
User=deploy
Group=deploy
WorkingDirectory=/home/deploy/myapp
# This is the new line
EnvironmentFile=/home/deploy/myapp/.env
ExecStart=/home/deploy/venv/bin/gunicorn --workers 3 --bind unix:/home/deploy/myapp/myapp.sock myapp.wsgi:application
Restart=always
RestartSec=5
We added two things here. EnvironmentFile
points to our new .env
file. Systemd will read this file and make the variables available to the application process.
We also added RestartSec=5
. This tells systemd to wait 5 seconds before attempting a restart. This prevents a broken app that crashes immediately on startup from restarting in a tight loop and consuming all your server’s resources.
Systemd looks complex from the outside. But you do not need to understand all of it. For running your application you just need a simple service file. This one file gives you robust process management for free. It handles logging restarting and startup order. You do not need complex shell scripts or third party tools for the basics. It all comes down to one simple configuration file. Think about the ways you've kept processes running in the past and share your experience with the prompt below.
— Rishi Banerjee
September 2025