Putting Obskurnee behind a proxy

Last time I wrote about Obskurnee, a companion app for book clubs. Source code here. That post had basic info and docker setup instructions; this time, there's some info about the implementation, and instructions on reverse proxy config.

The stack

Obskurnee is meant to be run on Ubuntu Server, in Docker, behind nginx. (I.e. that's what I test and run it on; you do you.)

The app is built on .NET, v5.0 at the time of writing this, though I will probably upgrade to v6 when it's released. The frontend is built with VueJS 3, and will probably never be upgraded, because I've tried a few times, and it seems even minor updates to the JS libraries ruin everything forever. 😜 Data is stored in SQLite. Of course there's a bunch of libraries, but most of them are pretty ordinary.

On the .NET side, I just want to point out Flurl, my favorite way of composing requests, and a couple new discoveries: Markdig for rendering Markdown, and ImageSharp - an image processing library with no native dependencies, so it also runs in Docker on Ubuntu. I wanted to add "spoiler" support to Markdown, so Markdig's flexibility came in handy. I wrote about that here. (And I've really enjoyed how well the configuration middleware works. The standard .NET setup enables me to use JSON config files by default, override them with local developer secrets or environment-specific files, or override from environment variables, which solves Docker container config with zero effort on my part.)

One thing to note is that this is the first time I've worked with most of the libraries and technologies involved, especially on the frontend. It's by no means presented as a paragon of clean code everyone should aspire to. (I've progressively learned while writing the app and didn't rewrite if not necessary, because my free time is limited.)

Similarly, the data model/handling might seem a bit... odd in places. That's because the app started with LiteDB as its backend - another .NET library I really like. Worked great, except that when I tried hosting the app, it only worked on Windows, which is a no-go. After some hair-pulling, I've discovered the issue had been with the library I'd been using to store identity data in LiteDB. I've only been able to fix it when I replaced the data backend with SQLite. (Well, I could have kept everything in LiteDB and have a separate SQLite as identity backend, but I'd decided that's ugly, especially for such a small project.)

The problem with that: the app was already built on LiteDB, which is a document (no-sql) database, and SQLite is a relational one. Because of this impedance mismatch, I had to completely rewrite the data model and DB logic, and in some places the end result isn't as clean as it could have been if it were designed with SQL in mind from the get-go.

Nginx reverse proxy

You could use the bog-standard basic setup that just forwards requests, and most of the app would work. Some features use Signalr however, which runs on WebSockets, so the proxy needs to accommodate that. The config is taken from some official .NET samples, again nothing out of the ordinary.

So assuming you already have Obskurnee running on port 8080 (instructions in the git repo or the previous post) and want to run it behind nginx on obskurnee.mydomain.com:

map $http_connection $connection_upgrade {
    "~*Upgrade" $http_connection;
    default keep-alive;
}

server {
    server_name obskurnee.mydomain.com;

    location /hubs {
       proxy_pass http://127.0.0.1:8080;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection $connection_upgrade;
       proxy_cache off;
       proxy_buffering off;
       proxy_read_timeout 100s;

       proxy_set_header Host $host;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
     }

    location / {
       proxy_pass http://127.0.0.1:8080;

       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection keep-alive;
       proxy_set_header Host $host;
       proxy_cache_bypass $http_upgrade;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;

       proxy_connect_timeout 100s;
       proxy_read_timeout 100s;

       gzip_types application/json application/javascript;
       gzip_proxied no-cache no-store private expired auth;
     }
}

That should be all you need to do to host Obskurnee. Check out the rest of Obskurnee-related posts if you wish.