Need help setting up a location based proxy server

I’ve been trying in vane to set up a location based server, and I could need quite a bit of help.

My aim is to write one configuration file, where three different services are defined and can be accessed by adding different keywords to the server’s url. Sounds easy, and for most of you this will be a piece of cake, but I have been experimenting for nearly two days now without any success.

The first service should be Radicale, a CalDAV/CardDAV server. I managed to set up this file:

my-conf
upstream php-handler {
    server unix:/var/run/php/php-fpm.sock;
}

server {
    listen 443 ssl;
    server_name myserver.localnet;

    include snippets/my-certs.conf;


        # radicale:

        location /radicale {
            return 301 $scheme://$host/radicale/;
        }

        location /radicale/ {
                proxy_pass http://localhost:5232/;
                proxy_pass_header Authorization;
                proxy_set_header  X-Script-Name /radicale;
                proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header  X-Forwarded-Host $host;
                proxy_set_header  X-Forwarded-Port $server_port;
                proxy_set_header  X-Forwarded-Proto $scheme;
                proxy_set_header  Host $http_host;
                error_log /var/log/nginx/radicale.error.log info;
        }

}

This part is working, I can use the url “myserver.localnet/radicale” in calendar apps and the are being synchronized. Great!

Now comes the hard part:

The two other servers I want to integrate into that file are:

  • Karadav, a lightweigt file synching server,
  • Opodsync, a synching service for podcast apps.

Karadav is capable of providing its own service, so can also be configured with a “proxy_pass” directive in the nginx config file. At least in theory, but I did not manage to get it working correctly. :frowning:

The third service, Opodsync, does not provide a service by itself, so it needs a lot more of configuration.

I’d be immensely thankful if some kind soul would help me with this.

Hi @wehkah!

Can you share the config with what you have tried so far? Setting up a proxy for Karadav should be doable based on what your saying. Does Opodsync provide an UI or an endpoint you can query?

Thank you for your offer to help me! :slight_smile:

To answer your questions:

Opodsync: yes, it provides a browser UI in which you can look at your subscription and do minimalistic management. But in the meantime I was told that it is meant to be run in the root of a virtual host. That could be the reason why my experiments failed.

Karadav: It too provides a UI to manage your files, and it comes with a system service that can be controlled by systemd. The port it listens to can be chosen at will, of course collisions must be avoided. I tried the exact same simple structure as the one above shown for Radicale with just another location, of course, but it didn’t turn out reliable: every other page was “not found” or some other error occurred. Which is a bit strange, because there’s even one video (yes: exactly one!) on YT by someone called “MMC Captain” who uses the same setup in his video and his setup seems to work, while mine did not.

Both of these apps need a “config.local.php” inside their working directory, where the exact URL of the server is to be set, that will be re-sent to the browser. Without this setting, the browser will try to connect to “localhost” at some point.

Radicale: It uses a simple config file where its storage location is defined. Its port can also be set at will, and you can also connect directly to that port and use the CalDAV service.

So, below is the config I tried so far. Please don’t bang your head onto the table when you look at it, remember: I am a complete Nginx newbie! :wink:

My solution now is three different host files, each one defining a virtual host listening on different ports. Although that setup is working, I want to use only one host file listening on one port, when I open the server to the outside later.

You may ask yourself: “Why does this guy want to use Nginx as a reverse proxy anyway??”

It’s the certificates: AntennaPod, the Android app that I want to use with Opodsync, does not accept self signed certs. So I need a cert by LetsEncrypt. That’s easy, and by using Nginx, there will be just one app (Nginx) that deals with the certs and the encryption to the outside world and no other app needs to be configured for this.

Next question: “Why does he not simply use Nextcloud, that has all in one??”

Yes, that is true: NC provides CalDAV, CardDAV, and WebDAV service, and there is even a plugin app that provides podcast synching.

But sadly, NC is a super heavyweight! You need some serious computing power to make it work smoothly and to get a satisfying performance out of it. And it has lots and lots of dependencies, that need lots of space and RAM and CPU power themselves. That’s why I want to try these three lighter apps.

my-config
server {
    listen 443 ssl;
    server_name my.server;

    include snippets/my-certs.conf;


        # radicale:

        location /radicale { return 301 $scheme://$host/radicale/; }
        location /caldav   { return 301 $scheme://$host/radicale/; }

        location /radicale/ {
                proxy_pass http://localhost:5232/;
                proxy_pass_header Authorization;
                proxy_set_header  X-Script-Name /radicale;
                proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header  X-Forwarded-Host $host;
                proxy_set_header  X-Forwarded-Port $server_port;
                proxy_set_header  X-Forwarded-Proto $scheme;
                proxy_set_header  Host $http_host;
                error_log /var/log/nginx/radicale.error.log info;
        }

        # karadav:

#       location /karadav { return 301 $scheme://$host/karadav/; }
#       location /webdav  { return 301 $scheme://$host/karadav/; }

#       location /karadav/ {
#               root /mnt/data/karadav/www/;
#                proxy_pass http://localhost:54321/;
#               proxy_pass_header Authorization;
#                proxy_set_header  X-Script-Name /karadav;
#                proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
#                proxy_set_header  X-Forwarded-Host $host;
#                proxy_set_header  X-Forwarded-Port $server_port;
#                proxy_set_header  X-Forwarded-Proto $scheme;
#                proxy_set_header  Host $http_host;
#               error_log /var/log/nginx/karadav.error.log info;
#        }
 

       # opodsync:
#       location /opodsync { return 301 $scheme://$host/opodsync/; }
#       location /gpodder  { return 301 $scheme://$host/opodsync/; }
#        location /gpodder/ { return 301 $scheme://$host/opodsync/; }

#       location /opodsync/ {
#               client_max_body_size 100M;
#               client_body_buffer_size 1024k;
#               root /mnt/data/opodsync/server;
#               index index.php;
#               location ~ \.php$ {
#                       try_files $uri $uri/ /index.php;
#                       include fastcgi_params;
#                       fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
#                       fastcgi_param REDIRECT_STATUS 200;
#                       fastcgi_pass unix:/var/run/php/php-fpm.sock; 
#               }
#               error_log /var/log/nginx/opodsync.error.log info;
#       }

}

Looking at your config, I can see a potential issue in your karadav config location block in that you are using booth a root and proxy_pass directive together. This config is asking NGINX to act both as a web server and proxy within the same location block. I’d be surprised if NGINX starts with both of these directives next to each other, but even if it does, it’ll lead to some weird behaviour.

Looking at opodsync, you are configuring NGINX as a web server, not a proxy. At first glance there is nothing outright wrong with that config so like you mentioned, there is likely something related to your PHP config acting up.

Can you share the config you say is working?

Corrected! Must have been left over from one of my many experiments.

So, so true! :wink:

Yes, but to my knowledge this needs to be done that way, because the app does not provide a service on its own, like radicale and karadav. Opodsync needs to be served via a web server to offer its service. And according to what I was told, it needs to be run as/in the root dir. (What’s the correct way of expressing this?)

But I hope that there is a way of making it run with a location based directive, so that I can call it by the URL “https://my.server/opodsync”.

Below you’ll find the working server file, that I was able to put together. Remember: right now I am running three different virtual servers, reachable on a different port each. This is what I want to eliminate, by putting all three services into one server file.

server {

        server_name my.server;
        listen 445 ssl; 

        include snippets/my-certs.conf;

       client_max_body_size 50M;
        client_body_buffer_size 50M;
        
        root /mnt/data/opodsync/server;
        index index.php;

        location ~ /(\.ht|\.git|\.hg|\.svn|\.vs|data|bin|inc|vendor|README|VERSION|SECURITY.md|COPYING|composer.json|composer.lock) {
            # Returns 403
            deny all;
            #return 404;
        }
        location / {
                try_files $uri $uri/ /index.php;
        }

        location ~ \.php$ {
                try_files $uri $uri/ /index.php;
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param REDIRECT_STATUS 200;
                fastcgi_pass unix:/var/run/php/php-fpm.sock; 
        }

        #access_log /var/log/nginx/opodsync.access.log;
        error_log /var/log/nginx/opodsync.error.log info;

}

I think you might have only shared one of your server files! That being said, I would probably suggest having a server block for the opodsync service instead of trying to configure both your proxied services and the opodsync web server all inside the same server block.

My knowledge of nginx is not very funded yet. Could you point me to an example of what you mean?

Not quite, but I can try to write some pseudo code for what I have in mind :stuck_out_tongue:

...
server {
    location /caldav {
        proxy_pass caldav:port;
    }
    location /karadav {
        proxy_pass karadav:port;
    }
    location /opodsync {
        proxy_pass opodsync:port;
    }
}
server {
    listen 8088;
    opodsynd code;
...
}

This is a very simple barebones pseudocode example but hopefully it’s enough to illuminate what I mean :slight_smile:

It certainly is! Many thanks, that gives me the general idea.

Just to make sure I understand this correctly:

Are both server-blocks to be put into one file in “sites-available”? I did not know that was possible. Or did you mean I need to use two different files and link both of them to “sites-enabled”?

I assume the listening port inside the second server block needs to be same port as in the line proxy_pass opodsync:port; So in your example this should be proxy_pass opodsync:8088;, right?

1 Like

That file structure is from Debian’s packaged version of NGINX, so I am not entirely familiar, sorry! I would suggest installing NGINX from NGINX’s own package repository nginx: Linux packages.

In any case, you should be able to use two server blocks in the same config file. At the end of the day, NGINX “compiles” all config files together when it starts/reloads. Having different config files is mostly for organization/readability purposes. NGINX doesn’t really care as long as config files are included in the appropriate context.

Yup! I should also note just in case it wasn’t obvious that the opodsync in proxy_pass opodsync:port; (same goes for the other services) needs to be replaced with the respective IP of the services. If you are running everything in the same environment localhost might work, but still worth keeping in mind :slight_smile:

Actually, I should have known that, because when redirecting the unencrypted port 80 to encrypted port 443, the same structure is used: two server blocks in one file. Stupid me! :man_facepalming:

Anyway: I will see to it in the next few days and I will let you know the result. Many, many thanks already for your patience and help! :slightly_smiling_face:

2 Likes

It sounds like you’re on the right track — setting up multiple services under one configuration can be tricky at first. For Radicale, make sure each service block has its own location directive inside your main server block. For example, something like:

location /radicale/ {
    proxy_pass http://localhost:5232/;
}

Then repeat similar blocks for your other two services with unique paths. That way, each one is accessible via different keywords in your URL. Once that’s set, restart your server and test each endpoint — it should start working smoothly.