VPS and LXD with NPM

Hosting multiple applications on a Virtual Private Server (VPS) is often problematic and expensive. Many companies and individuals who use VPS have a large monthly bill. LXD containers embedded in a VPS instance are highly efficient and can save on resources and expenses.

In this tutorial, we leveraged LXD containers on a private NAT network on a single VPS server instance sharing multiple web services via NginX Proxy Manager. We also installed and configured LXDWare LXD Dashboard to provide a management interface to the containers on the private NAT Network.

This solution could be deployed either for a VPS or for a portable enclave to customer sites. This solution still requires a interface address for a WAN connection or a VPS interface address and this solution will not function on a CGNAT.

First, stand up an Ubuntu 22.04 server instance on your VPS, VM, or bare metal.

Install LXD:

sudo snap install lxd

Initialize LXD:

sudo lxd init

Add the lxd group to your user account:

sudo usermod -aG lxd $(whoami)
newgrp lxd

Create a LXD Container to host NginX Proxy Manager (NPM):

lxc launch ubuntu:22.04 NPM -c boot.autostart=true -c security.nesting=true

Connect to your container:

lxc exec NPM bash

Add a user account:

adduser scott
usermod -aG docker scott

Install Docker and docker-compose and add your account to the docker group:

curl -sSL https://get.docker.com | sh
sudo apt install docker-compose
sudo usermod -aG docker scott
newgrp docker

Move to your account:

su - scott

Edit a docker-compose.yml file:

nano docker-compose.yml

Insert the following in the file:

version: '3'
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
      - '80:80'
      - '81:81'
      - '443:443'
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt

Save the file with CTRL O and enter and then CTRL X.

Start NPM:

docker-compose up -d

Exit the NPM LXD:


Create port forwards for your NPM container adjusting for your addresses:

lxc network forward create lxdbr0
lxc network forward port add lxdbr0 tcp 80 80
lxc network forward port add lxdbr0 tcp 443 443
lxc network forward port add lxdbr0 tcp 81 81

You will now be able to get to NPM at your LXD host address at port 81. In my example:

I have a tutorial entitled LXD Dashboard. Here are the LXD Dashboard Notes

Be sure to change the PHP settings for PHP 8.1

Instead of bridging the LXD container as in the linked notes, create the container for LXD Dashboard as follows:

lxc launch ubuntu:22.04 LXD-Dashboard -c boot.autostart=true

Find the NAT address for the LXD Dashboard and add a port forward like the following adjusting your addresses accordingly.

lxc network forward port add lxdbr0 tcp 8080 80

At this point, you should be able to reach and configure your LXD Dashboard at the address of your LXD server at port 8080.

Go to your DNS provider and create an A-Record for your domain name that points to the address of your VPS server or your WAN address whichever is applicable. Also create a CNAME subdomain record for whiteboard.yourdomain.com.

Create a LXD container for the Whiteboard app:

lxc launch ubuntu:22.04 Whiteboard -c boot.autostart=true -c security.nesting=true

Connect to the new container:

lxc exec Whiteboard bash

Update the container:

apt update && apt upgrade -y

Install Docker:

curl -sSL https://get.docker.com | sh

create a user account:

adduser scott
usermod -aG sudo scott
usermod -aG docker scott

Move to the new account:

su - scott

Run the Whiteboard app.

docker run -d --restart unless-stopped --name=Whiteboard -p 80:8080 rofl256/whiteboard

Follow the rest of the tutorial to make an entry in NPM for the Whiteboard subdomain and SSL certificate.

You can add many apps following the pattern of the Whiteboard app. The only real difference between this approach and my “bridge0” approach is that this uses all LXD NAT addresses for the containers. That makes having multiple apps in isolated LXD environments possible on one single VPS server.

LXD likes memory. However, 20 LXD containers with apps use less memory than 20 servers or 20 virtual machines.