FastAPINginxInterserverUvicornSystemd

    Running My FastAPI Application Properly with systemd: Part-2

    In the previous part, I deployed my FastAPI application on an Ubuntu VPS and used nohup to keep Uvicorn running after closing the SSH connection. That worked, but I wanted to do it...

    Sep 6, 2026
    6 min read
    6 views
    Running My FastAPI Application Properly with systemd: Part-2

    In the previous part, I deployed my FastAPI application on an Ubuntu VPS and used nohup to keep Uvicorn running after closing the SSH connection.

    That worked, but I wanted to do it in a proper way.

    If I use nohup, I need to manually start the application again if the server restarts. Also, I need to manually manage the process.

    So this time, I decided to use systemd.

    Why I wanted to move away from nohup

    Earlier, I was running my application like this:

    bash
    nohup uvicorn app.main:app --host 0.0.0.0 --port 8000 > app.log 2>&1 &

    This was useful for quickly running the application in the background.

    But for a server, I wanted something that could properly manage my application.

    With systemd, I can:

    • Start the application
    • Stop the application
    • Restart the application
    • Check its status
    • View logs
    • Automatically start the application after a server reboot
    • Automatically restart it if it stops unexpectedly

    So systemd felt like the better option.


    1. Stop the old Uvicorn process

    First, I checked whether Uvicorn was still running:

    bash
    ps aux | grep uvicorn

    I found the running process:

    text
    root ... /root/projects/backend/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000

    I stopped it using its process ID:

    bash
    kill 33893

    Then I checked again to make sure the old process was gone.

    At this point, I didn't want to start Uvicorn with nohup again.


    2. Create a systemd service

    I created a new service file:

    bash
    sudo nano /etc/systemd/system/pocket-flow.service

    Then I added:

    ini
    [Unit]
    Description=Pocket Flow FastAPI Application
    After=network.target
    
    [Service]
    User=root
    WorkingDirectory=/root/projects/backend/pocket-flow-backend
    ExecStart=/root/projects/backend/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000
    Restart=always
    RestartSec=5
    
    [Install]
    WantedBy=multi-user.target

    There are a few things here that I wanted to understand.

    WorkingDirectory

    ini
    WorkingDirectory=/root/projects/backend/pocket-flow-backend

    This tells systemd where my application is located.

    ExecStart

    ini
    ExecStart=/root/projects/backend/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000

    Here I'm directly using the Uvicorn inside my virtual environment.

    This is important because systemd doesn't automatically activate my Python virtual environment like I do in an SSH terminal.

    Instead of doing:

    bash
    source venv/bin/activate

    I can directly use:

    text
    /root/projects/backend/venv/bin/uvicorn

    Restart=always

    ini
    Restart=always

    This tells systemd to restart the application if it stops.

    I also added:

    ini
    RestartSec=5

    so it waits a few seconds before trying again.


    3. Why I changed 0.0.0.0 to 127.0.0.1

    Previously, I was running:

    bash
    --host 0.0.0.0

    This allows Uvicorn to listen on all network interfaces.

    But now I already have Nginx handling the public requests.

    So I changed it to:

    bash
    --host 127.0.0.1

    Now Uvicorn only listens locally.

    The flow is:

    text
                             Internet
                                │
                                ↓
                  api.pocket-flow.jotech.in
                                │
                                ↓
                         162.35.175.67
                                │
                                ↓
                            Nginx :443
                                │
                                ↓
                       127.0.0.1:8000
                                │
                                ↓
                            Uvicorn
                                │
                                ↓
                             FastAPI

    Nginx is the public-facing server, while Uvicorn only needs to receive requests from Nginx on the same server.

    I liked this setup better because I don't need to expose the application server directly when Nginx is already handling the incoming web traffic.


    4. Reload systemd

    After creating the service file, I needed to tell systemd about it:

    bash
    sudo systemctl daemon-reload

    There was no output.

    That's normal.

    It means systemd reloaded its service configuration.


    5. Start the FastAPI application

    Then I started my service:

    bash
    sudo systemctl start pocket-flow

    I checked the status:

    bash
    sudo systemctl status pocket-flow

    And I got:

    text
    Active: active (running)

    So my FastAPI application was running successfully.

    The status also showed:

    text
    Uvicorn running on http://127.0.0.1:8000

    This was exactly what I wanted.


    6. Check the application logs

    One useful thing about systemd is that I don't have to depend on the app.log file I used with nohup.

    I can see the application logs using:

    bash
    journalctl -u pocket-flow

    I can also follow the logs while the application is running:

    bash
    journalctl -u pocket-flow -f

    This is useful when I make a change and want to see what happened after restarting the application.


    7. Automatically start after a server reboot

    One of the main reasons I wanted to use systemd was to avoid manually starting my application after every server restart.

    I enabled the service:

    bash
    sudo systemctl enable pocket-flow

    In my case, the service status already showed:

    text
    enabled

    So systemd will start my FastAPI application automatically when the Ubuntu server boots.

    This is one of the main differences compared with the nohup approach I was using before.


    8. Useful commands I can use now

    Now I can manage my application with simple commands.

    Start

    bash
    sudo systemctl start pocket-flow

    Stop

    bash
    sudo systemctl stop pocket-flow

    Restart

    bash
    sudo systemctl restart pocket-flow

    Check status

    bash
    sudo systemctl status pocket-flow

    View logs

    bash
    sudo journalctl -u pocket-flow

    Follow logs

    bash
    sudo journalctl -u pocket-flow -f

    Check if it starts automatically

    bash
    sudo systemctl is-enabled pocket-flow

    9. What changed from Part 1?

    In Part 1, I had:

    text
    Nginx
       ↓
    Uvicorn
       ↓
    FastAPI

    But Uvicorn was being managed manually with nohup.

    Now systemd manages Uvicorn:

    text
                             Internet
                                │
                                ↓
                  api.pocket-flow.jotech.in
                                │
                                ↓
                            Nginx :443
                                │
                                ↓
                       127.0.0.1:8000
                                │
                                ↓
                          systemd
                                │
                                ↓
                            Uvicorn
                                │
                                ↓
                             FastAPI

    The important difference is that systemd is now responsible for keeping my FastAPI application running.


    What I learned from Part 2

    The biggest thing I understood from this part is that nohup is useful when I just want to keep a process running after closing SSH, but systemd makes much more sense when I'm actually running an application on a server.

    I also learned that I don't need to activate my virtual environment every time when systemd starts the application. I can directly point ExecStart to the Uvicorn inside my virtual environment.

    Now my FastAPI application can start automatically with the server, restart when needed, and I have a proper place to check the logs.

    This is a small step, but it makes the deployment feel much more like a proper server setup rather than something I manually started from an SSH terminal.

    J
    Written by

    Jobi S S

    Portfolio

    admin

    Sharing technical insights, engineering concepts, and practical modern software development guides.

    Community Discussion

    Enjoyed this read? Show your support or share your thoughts.

    Comments (0)

    No comments yet. Be the first to comment!

    📬 Enjoyed this article?

    Get new posts on Django, FastAPI, and system design straight to your inbox. No spam — unsubscribe whenever you want.