this post was submitted on 10 Jan 2025
84 points (96.7% liked)

Selfhosted

41132 readers
322 users here now

A place to share alternatives to popular online services that can be self-hosted without giving up privacy or locking you into a service you don't control.

Rules:

  1. Be civil: we're here to support and learn from one another. Insults won't be tolerated. Flame wars are frowned upon.

  2. No spam posting.

  3. Posts have to be centered around self-hosting. There are other communities for discussing hardware or home computing. If it's not obvious why your post topic revolves around selfhosting, please include details to make it clear.

  4. Don't duplicate the full text of your blog or github here. Just post the link for folks to click.

  5. Submission headline should match the article title (don’t cherry-pick information from the title to fit your agenda).

  6. No trolling.

Resources:

Any issues on the community? Report it using the report flag.

Questions? DM the mods!

founded 2 years ago
MODERATORS
 

tldr: I'd like to set up a reverse proxy with a domain and an SSL cert so my partner and I can access a few selfhosted services on the internet but I'm not sure what the best/safest way to do it is. Asking my partner to use tailsclae or wireguard is asking too much unfortunately. I was curious to know what you all recommend.

I have some services running on my LAN that I currently access via tailscale. Some of these services would see some benefit from being accessible on the internet (ex. Immich sharing via a link, switching over from Plex to Jellyfin without requiring my family to learn how to use a VPN, homeassistant voice stuff, etc.) but I'm kind of unsure what the best approach is. Hosting services on the internet has risk and I'd like to reduce that risk as much as possible.

  1. I know a reverse proxy would be beneficial here so I can put all the services on one box and access them via subdomains but where should I host that proxy? On my LAN using a dynamic DNS service? In the cloud? If in the cloud, should I avoid a plan where you share cpu resources with other users and get a dedicated box?

  2. Should I purchase a memorable domain or a domain with a random string of characters so no one could reasonably guess it? Does it matter?

  3. What's the best way to geo-restrict access? Fail2ban? Realistically, the only people that I might give access to live within a couple hundred miles of me.

  4. Any other tips or info you care to share would be greatly appreciated.

  5. Feel free to talk me out of it as well.

(page 2) 50 comments
sorted by: hot top controversial new old
[–] greylinux@lemm.ee 2 points 1 week ago

I used to do a reverse proxy setup with caddy , but now I self host a Wireguard VPN. It has access to Nextcloud on the same machine, Home Assistant and Kodi on another. On our phones, Wireguard only has access to certain apps the rest of the network traffic is normal, so a nice simple setup.

[–] jimmy90@lemmy.world 2 points 1 week ago (1 children)

nixos with nginx services does all proxying and ssl stuff, fail2ban is there as well

[–] a_fancy_kiwi@lemmy.world 1 points 1 week ago (1 children)

I know I should learn NixOS, I even tried for a few hours one evening but god damn, the barrier to entry is just a little too high for me at the moment 🫤

[–] jimmy90@lemmy.world 1 points 1 week ago* (last edited 1 week ago) (1 children)

i guess you were able to install the os ok? are you using proxmox or regular servers?

i can post an example configuration.nix for the proxy and container servers that might help. i have to admit debugging issues with configurations can be very tricky.

in terms of security i was always worried about getting hacked. the only protection for that was to make regular backups of data and config so i can restore services, and to create a dmz behind my isp router with a vlan switch and a small router just for my services to protect the rest of my home network

[–] a_fancy_kiwi@lemmy.world 1 points 1 week ago (5 children)

i guess you were able to install the os ok? are you using proxmox or regular servers?

I was. It was learning the Nix way of doing things that was just taking more time than i had anticipated. I'll get around to it eventually though

I tried out proxmox years ago but besides the web interface, I didn't understand why I should use it over Debian or Ubuntu. At the moment, I'm just using Ubuntu and docker containers. In previous setups, I was using KVMs too.

Correct me if I'm wrong, but don't you have to reboot every time you change your Nix config? That was what was painful. Once it's set up the way you want, it seemed great but getting to that point for a beginner was what put me off.

I would be interested to see the config though

[–] jimmy90@lemmy.world 2 points 1 week ago* (last edited 1 week ago)

you only need to reboot Nix when something low level has changed. i honestly don't know where that line is drawn so i reboot quite a lot when i'm setting up a Nix server and then hardly reboot it at all from then on even with auto-updates running oh and if i make small changes to the services i just run sudo nixos-rebuild switch and don't reboot

[–] jimmy90@lemmy.world 2 points 1 week ago* (last edited 1 week ago)

this is my nginx config for my element/matrix services

as you can see i am using a proxmox NixOS with an old 23.11 nix channel but i'm sure the config can be used in other NixOS environments


{ pkgs, modulesPath, ... }:

{
  imports = [
    (modulesPath + "/virtualisation/proxmox-lxc.nix")
  ];

  security.pki.certificateFiles = [ "/etc/ssl/certs/ca-certificates.crt" ];

  system.stateVersion = "23.11";
  system.autoUpgrade.enable = true;
  system.autoUpgrade.allowReboot = true;

  nix.gc = {
    automatic = true;
    dates = "weekly";
    options = "--delete-older-than 14d";
  };

  networking.firewall.allowedTCPPorts = [ 80 443 ];

  services.openssh = {
    enable = true;
    settings.PasswordAuthentication = true;
  };

  users.users.XXXXXX = {
    isNormalUser = true;
    home = "/home/XXXXXX";
    extraGroups = [ "wheel" ];
    shell = pkgs.zsh;
  };

  programs.zsh.enable = true;

  security.acme = {
    acceptTerms = true;
    defaults.email = "XXXXXX@yahoo.com";
  };

  services.nginx = {
    enable = true;

    virtualHosts._ = {
      default = true;
      extraConfig = "return 500; server_tokens off;";
    };

    virtualHosts."XXXXXX.dynu.net" = {
      enableACME = true;
      addSSL = true;

      locations."/_matrix/federation/v1" = {
        proxyPass = "http://192.168.10.131:8008/";
        extraConfig = "client_max_body_size 300M;" +
          "proxy_set_header X-Forwarded-For $remote_addr;" +
          "proxy_set_header Host $host;" +
          "proxy_set_header X-Forwarded-Proto $scheme;";
      };

      locations."/" = {
        extraConfig = "return 302 https://element.xxxxxx.dynu.net/;";
      };

      extraConfig = "proxy_http_version 1.1;";
    };

    virtualHosts."matrix.XXXXXX.dynu.net" = {
      enableACME = true;
      addSSL = true;

      extraConfig = "proxy_http_version 1.1;";

      locations."/" = {
        proxyPass = "http://192.168.10.131:8008/";
        extraConfig = "client_max_body_size 300M;" +
          "proxy_set_header X-Forwarded-For $remote_addr;" +
          "proxy_set_header Host $host;" +
          "proxy_set_header X-Forwarded-Proto $scheme;";
      };
    };

    virtualHosts."element.XXXXXX.dynu.net" = {
      enableACME = true;
      addSSL = true;
      locations."/" = {
        proxyPass = "http://192.168.10.131:8009/";
        extraConfig = "proxy_set_header X-Forwarded-For $remote_addr;";
      };
    };

    virtualHosts."call.XXXXXX.dynu.net" = {
      enableACME = true;
      addSSL = true;
      locations."/" = {
        proxyPass = "http://192.168.10.131:8080/";
        extraConfig = "proxy_set_header X-Forwarded-For $remote_addr;";
      };
    };

    virtualHosts."livekit.XXXXXX.dynu.net" = {
      enableACME = true;
      addSSL = true;

      locations."/wss" = {
        proxyPass = "http://192.168.10.131:7881/";
#        proxyWebsockets = true;
        extraConfig = "proxy_http_version 1.1;" +
          "proxy_set_header X-Forwarded-For $remote_addr;" +
          "proxy_set_header Host $host;" +
          "proxy_set_header Connection \"upgrade\";" +
          "proxy_set_header Upgrade $http_upgrade;";
      };

      locations."/" = {
        proxyPass = "http://192.168.10.131:7880/";
#        proxyWebsockets = true;
        extraConfig = "proxy_http_version 1.1;" +
          "proxy_set_header X-Forwarded-For $remote_addr;" +
          "proxy_set_header Host $host;" +
          "proxy_set_header Connection \"upgrade\";" +
          "proxy_set_header Upgrade $http_upgrade;";
      };
    };

    virtualHosts."livekit-jwt.XXXXXX.dynu.net" = {
      enableACME = true;
      addSSL = true;
      locations."/" = {
        proxyPass = "http://192.168.10.131:7980/";
        extraConfig = "proxy_set_header X-Forwarded-For $remote_addr;";
      };
    };

    virtualHosts."turn.XXXXXX.dynu.net" = {
      enableACME = true;
      http2 = true;
      addSSL = true;
      locations."/" = {
        proxyPass = "http://192.168.10.131:5349/";
      };
    };

  };
}




[–] jimmy90@lemmy.world 2 points 1 week ago* (last edited 1 week ago)

this is my container config for element/matrix podman containers do not run as root so you have to get the file privileges right on the volumes mapped into the containers. i used top to find out what user the services were running as. you can see there are some settings there where you can change the user if you are having permissions problems




{ pkgs, modulesPath, ... }:

{

  imports = [
    (modulesPath + "/virtualisation/proxmox-lxc.nix")
  ];

  security.pki.certificateFiles = [ "/etc/ssl/certs/ca-certificates.crt" ];

  system.stateVersion = "23.11";
  system.autoUpgrade.enable = true;
  system.autoUpgrade.allowReboot = false;

  nix.gc = {
    automatic = true;
    dates = "weekly";
    options = "--delete-older-than 14d";
  };

  services.openssh = {
    enable = true;
    settings.PasswordAuthentication = true;
  };

  users.users.XXXXXX = {
    isNormalUser = true;
    home = "/home/XXXXXX";
    extraGroups = [ "wheel" ];
    shell = pkgs.zsh;
  };

  programs.zsh.enable = true;

  environment.etc = {
    "fail2ban/filter.d/matrix-synapse.local".text = pkgs.lib.mkDefault (pkgs.lib.mkAfter ''
      [Definition]
      failregex = .*POST.* - <HOST> - 8008.*\n.*\n.*Got login request.*\n.*Failed password login.*
                  .*POST.* - <HOST> - 8008.*\n.*\n.*Got login request.*\n.*Attempted to login as.*\n.*Invalid username or password.*
    '');
  };

  services.fail2ban = {
    enable = true;
    maxretry = 3;
    bantime = "10m";
    bantime-increment = {
      enable = true;
      multipliers = "1 2 4 8 16 32 64";
      maxtime = "168h";
      overalljails = true;
    };
    jails = {
      matrix-synapse.settings = {
        filter = "matrix-synapse";
        action = "%(known/action)s";
        logpath = "/srv/logs/synapse.json.log";
        backend = "auto";
        findtime = 600;
        bantime  = 600;
        maxretry = 2;
      };
    };
  };

  virtualisation.oci-containers = {
    containers = {

      postgres = {
        autoStart = false;
        environment = {
          POSTGRES_USER = "XXXXXX";
          POSTGRES_PASSWORD = "XXXXXX";
          LANG = "en_US.utf8";
        };
        image = "docker.io/postgres:14";
        ports = [ "5432:5432" ];
        volumes = [
          "/srv/postgres:/var/lib/postgresql/data"
        ];
        extraOptions = [
          "--label" "io.containers.autoupdate=registry"
          "--pull=newer"
        ];
      };

      synapse = {
        autoStart = false;
        environment = {
          LANG = "C.UTF-8";
#          UID="0";
#          GID="0";
        };
 #       user = "1001:1000";
        image = "ghcr.io/element-hq/synapse:latest";
        ports = [ "8008:8008" ];
        volumes = [
          "/srv/synapse:/data"
        ];
        log-driver = "json-file";
        extraOptions = [
          "--label" "io.containers.autoupdate=registry"
          "--log-opt" "max-size=10m" "--log-opt" "max-file=1" "--log-opt" "path=/srv/logs/synapse.json.log"
          "--pull=newer"
        ];
        dependsOn = [ "postgres" ];
      };

      element = {
        autoStart = true;
        image = "docker.io/vectorim/element-web:latest";
        ports = [ "8009:80" ];
        volumes = [
          "/srv/element/config.json:/app/config.json"
        ];
        extraOptions = [
          "--label" "io.containers.autoupdate=registry"
          "--pull=newer"
        ];
#        dependsOn = [ "synapse" ];
      };

      call = {
        autoStart = true;
        image = "ghcr.io/element-hq/element-call:latest-ci";
        ports = [ "8080:8080" ];
        volumes = [
          "/srv/call/config.json:/app/config.json"
        ];
        extraOptions = [
          "--label" "io.containers.autoupdate=registry"
          "--pull=newer"
        ];
      };

      livekit = {
        autoStart = true;
        image = "docker.io/livekit/livekit-server:latest";
        ports = [ "7880:7880" "7881:7881" "50000-60000:50000-60000/udp" "5349:5349" "3478:3478/udp" ];
        cmd = [ "--config" "/etc/config.yaml" ];
        entrypoint = "/livekit-server";
        volumes = [
          "/srv/livekit:/etc"
        ];
        extraOptions = [
          "--label" "io.containers.autoupdate=registry"
          "--pull=newer"
        ];
      };

      livekitjwt = {
        autoStart = true;
        image = "ghcr.io/element-hq/lk-jwt-service:latest-ci";
        ports = [ "7980:8080" ];
        environment = {
          LK_JWT_PORT = "8080";
          LIVEKIT_URL = "wss://livekit.xxxxxx.dynu.net/";
          LIVEKIT_KEY = "XXXXXX";
          LIVEKIT_SECRET = "XXXXXX";
        };
        entrypoint = "/lk-jwt-service";
        extraOptions = [
          "--label" "io.containers.autoupdate=registry"
          "--pull=newer"
        ];
      };

    };
  };

}




[–] jimmy90@lemmy.world 1 points 1 week ago

yeah proxmox is not necessary unless you need lots of separate instances to play around with

load more comments (1 replies)
[–] Evotech@lemmy.world 2 points 1 week ago (3 children)
[–] AMillionMonkeys@lemmy.world 1 points 1 week ago (1 children)
[–] possiblylinux127@lemmy.zip 1 points 1 week ago* (last edited 1 week ago) (1 children)
[–] AMillionMonkeys@lemmy.world 1 points 1 week ago (1 children)
[–] possiblylinux127@lemmy.zip 1 points 1 week ago (1 children)
[–] Dirk@lemmy.ml 1 points 1 week ago

Smith & Wesson

load more comments (2 replies)
[–] j4k3@lemmy.world 2 points 1 week ago (4 children)

I've tried 3 times so far in Python/gradio/Oobabooga and never managed to get certs to work or found a complete visual reference guide that demonstrates a complete working example like what I am looking for in a home network. (Only really commenting to subscribe to watch this post develop, and solicit advice:)

load more comments (4 replies)
[–] Asparagus0098@sh.itjust.works 2 points 1 week ago

I use traefik with a wildcard domain pointing to a Tailscale IP for services I don't want to be public. For the services I want to be publicly available I use cloudflare tunnels.

load more comments
view more: ‹ prev next ›