A neater Proxmox no subscription setup
Lots of users run Proxmox suite of products with no support license and as long as they understand the caveats of freely available packages, there is nothing wrong with that. A major “post-install” chore no to forget is setting up the no-subscription repositories - otherwise users will be left with no updates whatsoever. Many also opt to look for a solution to get rid of the infamous “No valid subscription” notice popup, also dubbed as a nag - for a reason.
This post will explore a robust solution to all of this - now conveniently packaged into a no-subscription tool - that is meant to do one thing and do it well - and more importantly, will have its innards explained below.
Security risk
Having to deal with chores like this is somewhat inexplicable given that Proxmox distribute their suite under a FREE license - which grants everyone freedom to modify it as they please. This simply means users WILL take advantage of third-party solutions and are then often left in the dark what those actually do to their otherwise stock install. In turn, Proxmox open up their unpaid user base to third-party associated security risks over something that could have been resolved with a simple acknowledgment checkbox.
What not to do
This post exists to encourage better understanding of the system and what a user runs on it. The last thing one wants to end up with is having stealth third-party recurrent scripts on their system that they do not even comprehend.
Warning
Never download unknown code without having (someone trusted) had a good look at it. No exceptions.
Notably, whatever a user ends up running on a Proxmox host is running under the root privileges due to the architecture of Proxmox suite of products.
Issues with standalone scripts
There is a major caveat to using a simple script for a problem at hand here: effects of it will NOT persist, changes would get overwritten during upgrades from official repositories. Any “one-off” script that does NOT have this drawback uses its “single run” to install something onto the system that then runs recurrently. There goes the popular perception of a mere script being less intrusive to the system then a more complex solution.
The popular post-install script
Merely due to number of inquiries - NOT because it does something particularly worse than others - we will have a look at the now community maintained post-install script found under the misc category and delve right into its source on GitHub.
Note
This analysis was performed with code as retrieved April 12, 2025.
We will ONLY focus on one of its parts, namely: removing the ’no-subscription’ popup. It can be found below a line: msg_info "Disabling subscription nag"
There’s the code of our interest - which we will dissect:
echo "DPkg::Post-Invoke { \"...\"; };" >/etc/apt/apt.conf.d/no-nag-script
apt --reinstall install proxmox-widget-toolkit &>/dev/null
And the inner part (...
) was (reformatted - all from that single line, escaping NOT retained):
dpkg -V proxmox-widget-toolkit | grep -q '/proxmoxlib\.js$'
if [ $? -eq 1 ]; then {
echo 'Removing subscription nag from UI...';
sed -i '/.*data\.status.*{/{s/\!//;s/active/NoMoreNagging/}' \
/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js;
}; fi
APT hook
The first part adds a DPkg::Post-Invoke
like into a file under /etc/apt/apt.conf.d/
- this belongs to APT hooks that are then run (as per the rule) virtually every time APT runs on the system, for whichever reason, over all packages. Such scripts (the content) have to be scrutinised as they are basically living on on the system and run under root privileges, regularly.
Note
There also does not appear to be any built-in way to remove this script, while its presence after having run the script (if only once) is concealed from the user.
Following addition of this inner (recurrent) script onto the system, ir proceeds to (once) reinstall original a Proxmox package - this might look counter-intuitive, but it actually relies on the (now installed) recurrent script to do its work already on the APT run (i.e. hence the hook designation), i.e. the reinstall will be subject to the inner script already.
sed command
The inner script - this will be run regularly, e.g. after updates - checks result of dpkg -V
, i.e. integrity of the same Proxmox package, more specifically it will find out whether a file named proxmoxlib.js
has been tampered with (compared with the stock version that comes with the package).
The conditional if
statement only executes the sed
inside if the package is intact (stock). This is a wide assumption - that nothing else is e.g. patching the same file - as otherwise it would not do anything. The idea was to only execute the patching when the target file has changed, but unfortunately the APT hooks were absolutely not designed for this kind of problem and so there’s no knowing what exactly APT was altering on the system - hence the conditional check. The script runs every time APT runs, but only shows the announcement when it is about to patch anything.
JavaScript patched
The actual sed
run is beyond the scope of this post, but it is a method of replacing strings through the use of so-called regular expressions, i.e. the file proxmoxlib.js
will have its content altered on one specific line, changing it (<
out, >
in):
< .data.status.toLowerCase() !== 'active') {
---
> .data.status.toLowerCase() == 'NoMoreNagging') {
This affects the JavaScript component of Proxmox - a condition within - more of the surrounding JavaScript code can be seen in a separate post here regarding the no subscription notice removal - by a different method. A mere reformatting of the underlying target file might deem this regular expression ineffective, corrupt the JavaScript code or worse - change the logic without bringing in any obviously corrupt syntax and in an unintended piece of code.
Note
To reiterate, this is not a comparison of the sole script and the free-pmx package - which is about to be described - but whole range of approaches making use of APT hooks and sed substitutions on source files. If anything, this digression was meant to shed more light on what “just a script” can bring onto the system. Other approaches might e.g. install cron jobs or install systemd services.
A better way
Meanwhile, Debian already provides a neat mechanism for handling a situation that was to be tackled above - by the packaging system itself. A Debian package can bring in its executables - which can be scripts and configuration, but also - importantly - explicitly declare its interest to be notified when other packages are altering specific files on the system. It is the system that decides when it will trigger actions implemented by the interested package and under no other than declared rules. When the package is removed, so are its components responsible for handling these triggers.
Myths around Debian packages
First, we will dispel some myths that might surround the seemingly complicated matters:
No dubious APT repository. A package can be installed manually - from a single downloaded file - without having to trust an unknown repository. This one-off approach will NOT keep it updated, but this is the safer way to run code from strangers. Installing a package simply means files will be copied into well-defined locations. The system will take care of removing those same files during uninstall.
Transparency. Debian package is essentially an archive, the lack of transparency might come if it contains compiled binaries, but packages that contain only cleartext payload are as transparent to the user as a compressed script.
Root access. It is true that a e.g. a postinst hook (not to be confused with APT hooks mentioned above) runs under root privileges when package is installed, but this is the case for any script run from through CLI in Proxmox stock system. Furthermore, these scripts have well-known place in the package archive and are easy to find - in fact, they should be the first ones of interest to audit.
Further, maintainers of Debian packages typically follow certain standards when creating them - if they want it to pass a check.
free-pmx-no-subscription
Checking thoroughly any scripts is vital. Debian packages that contain scripts are no different. For the sake of convenience, GitHub repository of free-pmx-no-subscription is available, but users should always be ready to dissect a package downloaded as already packaged .deb file first-hand - or package it themselves.
Tip
More on the structure of a Debian package, how to look into the archive and more are in a separate post specifically examining free-pmx-no-subscription v0.1 as an example.
The locations to examine:
DEBIAN/postinst
- contains the script that runs with the package installation when it directly launches the two user commands (below);DEBIAN/triggers
- list of files that when altered, e.g. by Proxmox upgrades, will launch thepostinst
script automatically;bin/
- user commands - actual scripts ofno-subscription
andno-nag
;usr/lib/free-pmx/
- shared partial scripts that are sourced (utilities) or run (sub-commands) from the user command scripts (more below);usr/share/free-pmx/no-subscription-aptsrcs/
- templates for APT sources’ files that will replace the stock originals;usr/share/free-pmx/no-nag-nag-patchdefs/
- patch definitions containing templates for search&replace-like substitutions;etc/free-pmx/no-subscription.conf
- default configuration file.
Conceptually, these are all BASH scripts relying on limited use of external tools, namely:
- perl with one-liners taking advantage of its capabilities when it comes to regular expressions;
- shasum to calculate checksums;
- gnupg to display APT repository key details;
- wget to download Proxmox release key from their site.
User commands
No direct filesystem actions are performed in the user command scripts per se, i.e. they process options and arguments, display output and make use of the sub-tools as necessary.
The actual declarations of all essential tokens can be found in the shared:
/usr/lib/free-pmx/no-subscription-common
This is used equally by both user commands as well as the postinst
script, which needs to recognise whether a file getting upgraded on the system is of its interest. This common component also provides for shared initialisation (checking configuration file, reading command options) of the user command runs.
Second (and last) much more streamlined shared component contains utility snippets, e.g. the Perl one-liners:
/usr/lib/free-pmx/no-subscription-util
The internal sub-tools (named out below) are purposely small and take all the inputs from their arguments, not environment. The exception is output formatting when file descriptors are explicitly defined for normal output, user-visible error messaging, original standard error output (suppressed) and potential debugging output.
no-subscription
User-level documentation can be found in the manual page.
There are two internal sub-tools that are called:
aptsrc-list-replace
to perform the actual repository sources file replacement; andaptsrc-key-check
to ensure that Proxmox release key is present on the system.
no-nag
User-level documentation can be found in the manual page.
This script relies only on patchdef-apply
sub-tool, which implements the individual patching of front-end related (JavaScript) file. The user command script determines which files are to be patched, the sub-tool runs the well-known Perl one-liner with single regular expression - see standalone post on patching the “No valid subscription” notice. All patches are applied in the same manner.
The difference within this implementation lies in the fact that there are more target files to patch and potentially more patches (per target) to be applied. Patches are defined in a series of modular .patchdef files held in usr/share/free-pmx/no-nag-nag-patchdefs/
. These definitions are pure variable based and refer to actual raw block files, which are further versioned. The versions correspond to the component (Proxmox package) from which they became applicable.
Patching
The reason for the “more elaborate” patching method lies in its robustness - either whole blocks of code are identified and replaced with both syntactically and semantically correct substitutes or the whole patch fails gracefully, i.e. the target file remains untouched. The worst case scenario allows for safe failing of e.g. some of the patches - a good reason to e.g. consciously look for updated definitions if need be, but by no means an operational necessity. This is why this tool aims to be reliable in the long run - even if left outdated.
Note
This has already proved itself sound when Proxmox decided to pretty-reformat their entire codebase in the latest releases of Debian 12 based products even as these kind of blanket changes would normally only be expected on a major version bump event.
The format of patch definitions also aims to provide educational background on Proxmox implementation details and encourage users to tinker with it some more - if they desire. Hopefully it will become playground for JavaScript aficionados down the road - as no more is required.
And before breaking off - a little example: a single patch definition targeting the annoying highlight of the ’no-subscription’ repository line - reveals that the original code is completely oblivious to the fact that Ceph is not highlighted.
Note
Arguably, this is a testament how actually this feature is - and has been, for years - focused on marketing outcomes rather than system integrity.
Before pwt-32-repos-list-line:
if (components[0].match(/\w+(-no-subscription|test)\s*$/i)) {
metaData.tdCls = 'proxmox-warning-row';
err = '<i class="fa fa-fw warning fa-exclamation-circle"></i> ';
let qtip = components[0].match(/no-subscription/)
? gettext('The no-subscription repository is NOT production-ready')
: gettext('The test repository may contain unstable updates')
;
After pwt-32-repos-list-line:
if (components[0].match(/\w+(test)\s*$/i)) {
metaData.tdCls = 'proxmox-warning-row';
err = '<i class="fa fa-fw warning fa-exclamation-circle"></i> ';
let qtip = gettext('The test repository may contain unstable updates');
There’s basically 2 target files only (for each product):
- Proxmox Widget Toolkit (hence ‘pwt’ in the patch names above), which is common; and
- a specific one for each PVE / PBS / PMG - targeting dashboards.
No other than JavaScript files are getting patched and therefore only GUI is targeted - of a single host that the tool is being used on. Essentially, it influences the view of the otherwise unchanged situation - the other parts of stack, especially API are completely original. In other words, it is completely possible to run just one node patched like this with no influence on the rest.
Also, if the user decides to activate a license, this is still possible in the GUI with absolutely no ill-effects and will behave as per original implementation.
Updates
Expect this post to be further updated with more details depending on your feedback.
Excuse limited formatting, absent referencing and missing media content.
Your feedback is welcome in the main GitHub repository.