26 декабря 2025 г.

VPS configuration for the low-end machines

Cogito, ergo sum

René Descartes in 1637 

Recently I was configuring the VPS server and learned a few things that I want to summarize below.

  • The cheapest options are here: https://lowendbox.com/blog/1-vps-1-usd-vps-per-month/. But be careful. It can be a machine with an IPv6 IP address only which I was unable to configure with WireGuard and Immich. It seems that I can continue only with the machine having the IPv4 address.
  • Requirements
    • RAM. I have a preference for Debian/Ubuntu. Today you need a minimum of 512 Mb. I was able to install Debian 12 with just 512 Mb. I heard that Debian 13 can also run with 512 Mb. But it wasn't available yet. So I haven't tested it. If you have 1 Gb you can install Ubuntu or something else. Freshly installed Debian 12 was consuming 92 Mb of RAM. After updating, installing some packages and restarting it was only 75 Mb consumed. Fresh Ubuntu 24.04 LTS was using 188 Mb. And after updating and restarting only 156 Mb.
      • 0.5 Gb is not a lot of memory, therefore I am creating the 1 Gb swapfile. Alternatively you can mount your own ISO disk with Debian (rare option, but it worked with Vultr) and to create 1-2 Gb swap partition.
        # 1 GiB swapfile
        fallocate -l 1G /swapfile
        chmod 600 /swapfile
        mkswap   /swapfile
        swapon   /swapfile
        echo '/swapfile none swap sw 0 0' >> /etc/fstab   # always mount
        # Kernel tuning
        cat << 'EOF' > /etc/sysctl.d/99-swap.conf
        vm.swappiness = 150        # conservative
        vm.vfs_cache_pressure = 75
        EOF
        sysctl --system
        free -h            # confirm Swap: 1.0G
        swapon --show      # priority  -2
        cat /proc/sys/vm/swappiness
        apt install zram-tools
        GRUB_CMDLINE_LINUX_DEFAULT="zswap.enabled=1"
        update-grub
         
    • Disk. Debian 12 occupied 1.6 Gb. Debian 13 occupied 1.1 Gb. Ubuntu 24.04 LTS occupied 3.9 Gb. My disk was only 5 Gb. So I was proceeding with installing the R package (+0.95 Gb) but not with the Shiny server. It failed with both Debian and Ubuntu machines. Taken together, we need a minimum 10 Gb, if we want something beyond OS and several tiny applications.
  • SSH
    • I connect to the VPS using PuTTY. It is possible to create a link for the connection and to modify the command line to log in automatically:
      • "C:\Program Files\PuTTY\putty.exe" user@XXX.XXX.XXX.XXX -pw YYY -P ZZZ 
    • After installation, I updated the system, installed main packages, changed the machine's name, created a user with sudo rights, and benchmarked the system:
      • (bold font shows what should be edited in the configuration files, or chosen in the menu)
        apt-get update
        apt-get upgrade

        apt install mc sudo btop

        #UTF for btop
        sudo dpkg-reconfigure locales
            choose    en_US.UTF-8 UTF-8
            choose default en_US.UTF-8.

        export LANG=en_US.UTF-8

        sudo hostnamectl set-hostname Cobra
        sudo nano /etc/hosts

        passwd
        adduser user
        usermod -aG sudo user

        wget -qO- yabs.sh | bash
    • Then I change settings for the SSH access. I installed endlessh, ufw, and moved SSH to port ZZZ (at ~30'000 value):
      • apt install endlessh ufw

        ss -tulpn | grep LISTEN
        ufw allow ZZZ comment 'ssh'
        ufw allow 22 comment 'endlessh'

        nano /etc/ssh/sshd_config
        Port ZZZ
        PermitRootLogin no


        systemctl restart sshd

        nano /etc/endlessh/config
        Port 22

        nano /usr/lib/systemd/system/endlessh.service
            Uncomment the line AmbientCapabilities=CAP_NET_BIND_SERVICE
            Comment PrivateUsers=true
            Comment InaccessiblePaths=/run /var

        setcap 'cap_net_bind_service=+ep' /usr/bin/endlessh

        systemctl --now enable endlessh
        systemctl status endlessh
        ufw enable
        ufw status numbered
         
  • R framework
    • Basic R with rvest package (+1.2 Gb)
      • apt install libcurl3-openssl-dev libxml2-dev r-base-core
        R
        install.packages('rvest')
        q()
    •  The Shiny server at port XXX (+0.5 Gb)
      • R
        install.packages('shiny')
        install.packages('rmarkdown')
        q()

        wget https://download3.rstudio.org/ubuntu-20.04/x86_64/shiny-server-1.5.23.1030-amd64.deb
        dpkg -i shiny-server-1.5.23.1030-amd64.deb
        ufw allow XXX comment 'Shiny'

        nano /etc/shiny-server/shiny-server.conf
          listen XXX;

        systemctl restart shiny-server
        systemctl status shiny-server
        ss -plut | grep -i shiny
        rm 
        shiny-server-1.5.23.1030-amd64.deb
      • Test it: http://IP:PORT/
      • Files are in /srv/shiny-server/ 
  • WireGuard
    • Install it to XXX port.
      • curl -O https://raw.githubusercontent.com/angristan/wireguard-install/master/wireguard-install.sh
        chmod +x wireguard-install.sh
        sudo ./wireguard-install.sh

        sudo systemctl status wg-quick@wg0
        sudo wg show
        wg-quick down wg0 && wg-quick up wg0
        sudo ufw allow XXX comment 'WireGuard'
        sudo ufw reload
        sudo ufw enable
        sudo ufw status verbose
    • I am using wgg.sh to assign friendly names to peers:
      • curl -O https://raw.githubusercontent.com/FlyveHest/wg-friendly-peer-names/refs/heads/master/wgg.sh
        chmod +x wgg.sh
        sudo ./wgg -u
    • It is straightforward how to add peers on the server (/wireguard-install.sh and adding friendly name with wgg.sh -u) and the client (Windows: get *.conf file with WinSCP and import to the Wireguard package; Android: just scan a barcode; Linux: nmcli connection import type wireguard file wg0-client-XXX.conf). 
    • Flat-file CMS. I had a nice experience with GRAV (44 Mb), WonderCMS (0.1 Mb), and Yellow (1 Mb). You just need to have caddy (or nginx) and php with required dependencies.
    • Immich (+0.9 Gb)
      • Install Docker and Immich
        • for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do sudo apt-get remove $pkg; done

          sudo apt-get update
          sudo apt-get install ca-certificates curl
          sudo install -m 0755 -d /etc/apt/keyrings
          sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
          sudo chmod a+r /etc/apt/keyrings/docker.asc


          # Add the repository to Apt sources:
          echo \
            "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
            $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
            sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
          sudo apt-get update

          sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
          sudo docker run hello-world
          sudo apt install docker.io docker-compose-plugin

          git clone https://github.com/immich-app/immich.git
          cd immich
          ./install.sh
          ufw allow 2283 comment 'Immich'
          wg-quick down wg0 && wg-quick up wg0
          systemctl restart docker
      •  Import images from Google.Photo TakeOut:
        • wget https://github.com/simulot/immich-go/releases/download/v0.27.0/immich-go_Linux_x86_64.tar.gz
          tar -xzf immich-go_Linux_x86_64.tar.gz
          ./immich-go --version
          # you can add option --dry-run to simulate it

          ./immich-go upload from-google-photos --server=http://XXX:YYY --api-key=d17TMP5shRaQvWmtU3scI0iUWjTkY6xw3HtXCU0UII --session-tag=TRUE --overwrite --include-unmatched ~/Takeout
      •  I brought 7zip archive with USB stick formatted to NTFS filesystem:
        • sudo apt install ntfs-3g p7zip-full
          lsblk
          sudo blkid
          sudo  mkdir /media/usb
          sudo mount /dev/sdb1 /media/usb
          sudo umount /media/usb
      • If Immich is installed behind the NAS, we need to connect the client and the server with Immich to the same WireGuard serves (VPS) as peers.
        • Connect both to WireGuards as peers.
        • Now when you have 2 configuration files, you need to modify peer information related to Immich server on the VPS server:
          • ### Client Immich
            [Peer]
            PresharedKey = BRING ONE FROM IMMICH SERVER
            PublicKey = 
            BRING ONE FROM IMMICH SERVER
            #IP of IMMICH server as it is known to WG
            AllowedIPs = 10.66.66.5/32,fd42:42:42::5/128
            #Keep connection alive
            PersistentKeepalive = 25
            #Address behind NAS in the local network and port for WG
            Endpoint = 192.168.0.250:50000
        • And information related to the VPS server on Immich server:
          • ### Client VPS
            [Peer]
            PublicKey = 
            BRING ONE FROM VPS SERVER
            PresharedKey = BRING ONE FROM VPS SERVER
            #External IP for VPS and port for WireGuard
            Endpoint = XXX:50000
            AllowedIPs = 0.0.0.0/0,::/0
            #Keep connection alive
            PersistentKeepalive = 25 
    • Cleaning and updating the environment. I am using this script as root (https://github.com/itrus/bash-scripts-NGS/blob/main/small.md#cleaning-the-linux-environment). But you need to comment parts (snap or Chrome) that you don't need otherwise it may fail. It allowed me to clean 0.3 Gb from the freshly installed and configured the system. :)