Run a Next.js/Node.js App in a LXC on Proxmox without Docker

post-thumb

Photo by Ole_CNX from iStock

Table Of Contents

On my main homelab server, I run Proxmox VE so that I can virtualize using containers and virtual machines. Since I prefer to run as many apps as possible in containers (LXC) on my Proxmox server to minimize resource usage, I go to extra lengths to avoid using Docker in the containers so that I am not running a container instead of another container. There is no need to add extra layers and even though you can run Docker in LXC in an unprivileged container, I found that I had various issues restoring backups of my LXCs in Proxmox.

One app that I have wanted to set up is Homepage , which is a dashboard where you can create links to various services, websites, etc. As I have grown the number of apps and services running on my network, having a nice page where I can organize quick access to the services has become a greater need. Services which have API integrations can pull down more detailed information of what is currently happening on that service. This functionality makes for a great dashboard experience especially since you can fully customize the layout by editing YAML files.

The Homepage authors have provide instructions on how to launch the Homepage app without using Docker . The instructions are simple enough to follow to get up and running even on a LXC in Proxmox. However, the next step I wanted to take was to set it to run on startup of the LXC.

Since LXCs are system containers, the same services, commands, and other features of a standard Linux installation are available for use. This means I can make use of systemd to launch the Homepage app on startup. Although creating a new systemd service file is not super difficult, I did struggle for a little while to get everything working properly to the run the app. Because of this struggle, I thought I would share my experience so you can get set up more quickly and with minimal frustration!

The process below may seem a bit involved, but you only need to do this once to get everything set up. So let us begin!

Note

You should be able to adapt these instructions to work other Next.js/Node.js apps but other apps may have their own dependencies not described in this guide.

Create the LXC

To minimize taking a bunch of screenshots, I simply will describe the options you may use for your Homepage app. You may deviate from some of the values below if you wish to allocate greater or fewer resources to the container, for instance.

Proxmox Create CT
  • Click on the “Create CT” button in Proxmox.
  • On the “General” tab, enter the desired “CT ID” such as 100. Choose a “Hostname” such as homepage. “Unprivileged container” is checked by default. I recommend using unprivileged containers whenever possible to increase the security of your container by minimizing permissions given to the container. “Nesting” is also enabled by default, but it is not necessary unless you wish to use Docker, for example, so you could uncheck the option if you like. Finally, enter the desired “Password”. Click “Next”.
  • On the “Template” tab, select the “Storage” location where your container template was downloaded. You will need to choose a “Template” to use for the container. If you have not downloaded any templates, you will need to do that first before creating a container. I will be using the latest Ubuntu LTS version for my template. You may choose an alternative template, but note that the steps in this guide may not necessarily work if you use a different Linux distro (I have not tested other distros). Click “Next”.
  • On the “Disks” tab, select the “Storage” location where you want your container to be created. The “Disk size” defaults to 8 GB but you can change this value. On my container, it seems to use about 1.6 GB so you could lower that 8 GB value but it does not hurt to have it higher than you need. No additional disk space is used if you set it to a higher amount, and you can increase the size later should you need to. Click “Next”.
  • On the “CPU” tab, you may set the number of “Cores”. You can likely get away with setting this to 1 or 2 cores but the amount of resources you need will depend on the hardware you are using and potentially how complex your dashboard has been configured. Click “Next”.
  • On the “Memory” tab, you may enter at least 1024 MB for both the “Memory” and “Swap” for the initial creation of the container. The reason you need at least 1 GB of RAM is because building the Homepage app requires additional RAM. Fortunately, monitoring CPU and RAM usage is easy to do from the “Summary” page of the container so you can make adjustments as you see fit. Click “Next”.
  • On the “Network” tab, you will need to select the “Bridge” that you wish to use for your container. I cannot give you a specific value to use for that setting because it depends on how you have configured the bridges on your Proxmox server. The value may be vmbr0 or some other similar value. If you have a bridge that is VLAN-aware, you can set the “VLAN tag” to the appropriate value. If you are not using the Proxmox firewall, you can uncheck the “Firewall” box if you like. Finally, you will need to configure your IPv4/IPv6 addresses. I typically use DHCP for IPv4 and create a dynamic IPv4 reservation in OPNsense so it is managed in a single location for my entire network. For IPv6, I have noticed that SLAAC works better than DHCP on my network even though I have OPNsense configured to utilize both DHCPv6 and SLAAC (with using the “Assisted” Router Advertisements option). Click “Next”.
  • On the “DNS” tab, you may leave the settings blank if you are running the container on the same network as the Proxmox host. If not, you will need to change the “DNS servers” to be the DNS server on the network where the container resides. For example, if the container is on the 192.168.20.1/24 network, you will want to use 192.168.20.1 as the DNS server (this assumes you have multiple networks configured where the interface/gateway for the network is set to x.x.x.1). Click “Next”.
  • Now you are finally done! Click on “Start after created” before clicking “Finish” so you can get started using your new container right away.

Update the Container

Log into your LXC via the “Shell” in Proxmox (you may also configure SSH if you like) using the root user.

First thing you should do is to update all of the packages so everything is up to date. To do this, simply enter the following commands:

apt update
apt dist-upgrade

Install Prerequisite Software

Since the goal is to deploy the Homepage app without Docker, you will need to install a few prerequisite software packages. Keep in mind that even with Docker, you would still need to install it so you cannot completely escape installing software before you get started. There are a couple of extra packages you need to install to run it without Docker but it is not difficult to install.

You will need to install Git in order to pull down the file for the Homepage app.

apt install git

Create a New User

Generally it is not recommended to run apps and services as the root user since it is a security risk. Even though the risk is lowered a bit when running inside a system container, I would still recommend creating a separate user since malicious activities could still wreak havoc to your app or service even if it is contained within the LXC.

To create a new user, enter the following command to create a user called homepage:

useradd -m -s /bin/bash homepage

Now create a password for the homepage user by entering:

passwd homepage

Login as the New User

You may log out of your root user account using the command:

logout

Enter your homepage user credentials. You should be in your home directory by default.

Install Prerequisite Software

Homepage is built with Next.js which is built on top of Node.js so you will need to install Node.js. I found that using the nvm (Node Version Manager) utility to be useful because you can easily select the latest version of Node.js. Using the Ubuntu’s package manager will only have outdated version of Node.js which may not work with newer apps and those versions are typically no longer supported.

To use nvm, use the commands below. You will need to update the first command with the current version of the script from the GitHub page :

wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source /home/homepage/.bashrc
nvm install node

The last command above installs the latest version of node.

The project prefers to uses pnpm rather than npm to deploy the Homepage app so I recommend doing the same to minimize potential issues of deploying via slightly different methods. To install pnpm, run the following:

wget -qO- https://get.pnpm.io/install.sh | sh -
source /home/homepage/.bashrc

Clone the Git Repository

You can download the code for Homepage using a Git command:

git clone https://github.com/gethomepage/homepage.git

Build the Project

Before setting up Homepage to run as a service in the background, I recommend making sure it runs properly first. Otherwise, your service will fail to start, and you might think you did something wrong with the systemd service.

Go to the homepage folder which was created inside your home folder after running the git command (via cd homepage).

Run the pnpm commands below:

pnpm install
pnpm build
pnpm start

If all goes well you should see that the web server has started on localhost:

ready - started server on 0.0.0.0:3000, url: http://localhost:3000

You will now be able to connect to your Homepage dashboard in your browser by using the hostname or IP address of the LXC container on your network!

Default Homepage Dashboard

Set up the Systemd Service

The last thing you will want to do is create a systemd service so that every time you start up the LXC container, it starts the Homepage app. Otherwise, you have to manually start the service whenever you restart the container or the Proxmox host itself, which will get frustrating very quickly.

Before continuing, run the following command:

which node

This will show you where the node command is located such as /home/homepage/.nvm/versions/node/v21.6.1/bin/node. Copy/paste that value into a text editor so you can keep track of that.

Next you will need to logout and back in as the root user (since the homepage user is not assigned to the sudo group to limit permissions).

One step that I had to do after struggling for a while is to create a symbolic link to the node command. For some reason, it cannot find the node command when executing the pnpm commands even after I tried to set the path in the systemd service. Even though pnpm runs fine without the symbolic link when running the command manually inside of the LXC, the systemd service has trouble locating it.

To create a symbolic link for the node command, you will need to use the location that was produced from the which node command:

ln -s /home/homepage/.nvm/versions/node/v21.6.1/bin/node /usr/bin/node

Note

If you upgrade the version of node, you will need to update the symbolic link to point to the new location.

Now the systemd service file can finally be created. You may use nano to open a blank new file:

nano /etc/systemd/system/homepage.service

Paste the following text into the editor. If you are using the shell in the Proxmox web interface, you will need to right click and choose “Paste” or use “Ctrl” + “Shift” + “V” to paste (note you also have to hit “Shift” unlike copying/pasting elsewhere on your system). Press “Ctrl” + “O” to save the file and “Ctrl” + “X” to exit the editor.

[Unit]
Description=Homepage Service
After=network.target
StartLimitIntervalSec=0

[Service]
Type=simple
Restart=on-failure
RestartSec=10
User=homepage
Group=homepage
WorkingDirectory=/home/homepage/homepage
ExecStartPre=/home/homepage/.local/share/pnpm/pnpm install
ExecStartPre=/home/homepage/.local/share/pnpm/pnpm build
ExecStart=/home/homepage/.local/share/pnpm/pnpm start 

[Install]
WantedBy=multi-user.target

Notice that in the ExecStartPre statement the pnpm install and pnpm build commands are included in the systemd service. These are not necessary to include after you have first built the project manually, but I recommend including the commands so that if you update the Homepage app, the new updates will get installed automatically.

To enable and start the systemd service:

systemctl enable homepage
systemctl start

If you do not see an error message returned, there is a good chance the service is running. To check the status enter:

systemctl status homepage

You should see that the service is running if everything was configured properly!

* homepage.service - Homepage Service
     Loaded: loaded (/etc/systemd/system/homepage.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2024-02-08 19:07:08 UTC; 46s ago
    Process: 9129 ExecStartPre=/home/homepage/.local/share/pnpm/pnpm install (code=exited, status=0/SUCCESS)
    Process: 9141 ExecStartPre=/home/homepage/.local/share/pnpm/pnpm build (code=exited, status=0/SUCCESS)
   Main PID: 9354 (pnpm)
      Tasks: 22 (limit: 76987)
     Memory: 97.2M
        CPU: 52.648s
     CGroup: /system.slice/homepage.service
             |-9354 /home/homepage/.local/share/pnpm/pnpm start
             |-9364 sh -c "next start"
             `-9365 node /home/homepage/homepage/node_modules/.bin/../next/dist/bin/next start

Feb 08 19:07:07 homepage-home pnpm[9152]:   <E2><94><94> css/dd916459193df04c.css               13.3 kB
Feb 08 19:07:07 homepage-home pnpm[9152]: <CE><BB>  (Server)  server-side renders at runtime (uses getInitialProps or getServerSideProps)
Feb 08 19:07:07 homepage-home pnpm[9152]: <E2><97><8B>  (Static)  automatically rendered as static HTML (uses no initial props)
Feb 08 19:07:07 homepage-home pnpm[9152]: <E2><97><8F>  (SSG)     automatically generated as static HTML + JSON (uses getStaticProps)
Feb 08 19:07:08 homepage-home systemd[1]: Started Homepage Service.
Feb 08 19:07:08 homepage-home pnpm[9354]: > [email protected] start /home/homepage/homepage
Feb 08 19:07:08 homepage-home pnpm[9354]: > next start
Feb 08 19:07:08 homepage-home pnpm[9365]: (node:9365) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
Feb 08 19:07:08 homepage-home pnpm[9365]: (Use `node --trace-deprecation ...` to show where the warning was created)
Feb 08 19:07:08 homepage-home pnpm[9365]: ready - started server on 0.0.0.0:3000, url: http://localhost:3000

The last line indicates the web server is running on port 3000 as when the pnpm commands were executed manually earlier in this guide. The Homepage app is now set up without using Docker!

Creating a separate user account and the systemd service certainly adds more steps to the process, but it is worth doing because you are running the app under a user with restricted permissions as well as automatically launching the app on startup of the container.

Updating the Homepage App

At this point, you do not need to do anything further to start using the Homepage app, but I wanted to mention how to update the app. The process

To update the Homepage app, you simply do the following to pull down code from the Git repository:

cd /home/homepage/homepage
git pull

Since the pnpm install and pnpm build commands are included in the systemd service file, you simply restart the service to automatically build and deploy the new version.

systemctl restart homepage
comments powered by Disqus

You May Also Like