A bash script for automating package updates across all LXC containers on a Proxmox VE host.
- Automatically updates all LXC containers, auto-detecting
apt,dnf, oryumper container at runtime - Handles stopped containers (starts them, updates, then stops)
- Prevents concurrent runs with
flock-based locking - Pre-flight internet connectivity check
- External exclude list configuration file
- Automatic log rotation (30-day retention)
- Results summary with success/failure/skipped counts
- Color-coded output
- Proxmox VE host
- Root access
- LXC containers with
apt,dnf, oryumavailable (Debian/Ubuntu, Fedora/RHEL/Rocky/Alma, and older CentOS/RHEL7-era containers are all supported). The package manager is auto-detected per container — no configuration needed, and a mix of distributions across containers is fine.
Clone the repo to your Proxmox host and run the installer:
git clone https://github.com/Slade-Bennett/Proxmox-LXC-Updater.git && cd Proxmox-LXC-Updater
chmod +x install.sh
sudo ./install.shThe installer will:
- Copy the script to
/usr/bin/lxc-update - Create
/etc/lxc-update/config directory - Create an empty exclude list file
- Create the log directory
-
Copy the script to your Proxmox host:
scp update.sh root@proxmox:/usr/bin/lxc-update chmod +x /usr/bin/lxc-update
-
Create the config directory:
mkdir -p /etc/lxc-update cp exclude.list.example /etc/lxc-update/exclude.list
Run manually:
lxc-update| Flag | Description |
|---|---|
-c, --container <CTID> |
Update only this container. Repeatable or comma-separated (-c 100,105). Bypasses the exclude list, since an explicit target is an explicit request. |
-n, --dry-run |
Show what would happen without starting/stopping containers or updating packages. |
-h, --help |
Show the help message. |
lxc-update --dry-run # preview a full run
lxc-update -c 105 # update just container 105, even if it's on the exclude listAdd to root's crontab for automatic weekly updates:
crontab -e# Run every Sunday at 3:00 AM
0 3 * * 0 /usr/bin/lxc-update >> /var/log/lxc-update/cron.log 2>&1Edit /etc/lxc-update/exclude.list and add one CTID per line:
# Lines starting with # are ignored
100
105
110
Or use the command line:
echo "100" >> /etc/lxc-update/exclude.listEvery path is overridable via environment variable, mainly useful for testing without touching real system paths:
| Variable | Env Var Override | Default | Description |
|---|---|---|---|
LOGDIR |
LXC_UPDATE_LOGDIR |
/var/log/lxc-update |
Log file directory |
LOCKFILE |
LXC_UPDATE_LOCKFILE |
/tmp/lxc-update.lock |
Lock file path |
EXCLUDE_FILE |
LXC_UPDATE_EXCLUDE_FILE |
/etc/lxc-update/exclude.list |
Exclude list file |
LOG_RETENTION_DAYS |
LXC_UPDATE_LOG_RETENTION_DAYS |
30 |
Days to keep log files |
CONTAINER_TIMEOUT |
LXC_UPDATE_CONTAINER_TIMEOUT |
600 |
Seconds allowed per package manager step before a container is treated as hung |
- Acquires exclusive lock using
flockto prevent concurrent runs - Cleans up log files older than 30 days
- Loads exclude list from
/etc/lxc-update/exclude.list - Verifies internet connectivity via ping to
8.8.8.8 - Retrieves list of all containers using
pct list - For each container:
- Skips if in exclude list
- Starts container if stopped (remembers original state)
- Waits for container to be ready (up to 10 seconds)
- Detects the package manager present (checks for
dnf, thenyum, thenapt-get, viacommand -vinside the container — not inferred from an OS name, so it stays correct even if a container's OS changes) - Updates packages using whichever manager was detected, each step bounded by a timeout so one hung container can't block the rest of the run:
- apt:
apt-get update,upgrade -y,autoremove -y, noninteractively (DEBIAN_FRONTEND=noninteractive, existing conffiles kept on conflict) - dnf:
dnf upgrade -y(refreshes metadata and upgrades in one step),dnf autoremove -y - yum:
yum update -y,yum autoremove -y
- apt:
- If no supported package manager is found, the container is counted as failed
- Restores original stopped state if applicable, including when the container fails to start or has no supported package manager
- Displays summary with success/failure/skipped counts
Starting LXC updates: Wed Jan 28 10:00:00 UTC 2026
Updating container 100...
Finished container 100
Updating container 101...
Finished container 101
Skipping container 105 (excluded)
Update complete: Wed Jan 28 10:05:00 UTC 2026
Results: 2 succeeded, 0 failed, 1 skipped
- Supports containers using
apt,dnf, oryum; containers using other package managers (e.g.apk,zypper,pacman) are skipped and counted as failed - Requires containers to have network access
- No parallel execution (updates containers sequentially)
This repo includes a Jenkinsfile that lints the script and runs a test suite against a mocked pct command — no real Proxmox host or containers involved, so it's safe to run on any Jenkins worker with shellcheck and Bats installed.
Pipeline stages: Lint (bash -n + shellcheck on both scripts) → Test (bats tests/, exercising exclude-list handling, stopped/running state restoration, apt/dnf/yum detection and failure paths, and unsupported-package-manager handling, all against tests/mocks/pct, a fake pct command scripted via environment variables).
Real end-to-end testing against actual LXC containers would require a Jenkins agent with pct access on the Proxmox host itself, which is a much bigger privilege grant than this pipeline needs — the mocked test suite is the intended way to validate logic changes.
| File | Description |
|---|---|
update.sh |
Main update script |
install.sh |
Installer script |
exclude.list.example |
Example exclude list template |
LICENSE |
MIT License |
| Path | Description |
|---|---|
/usr/bin/lxc-update |
Installed script |
/etc/lxc-update/exclude.list |
Container exclusion list |
/var/log/lxc-update/ |
Log directory |
/var/log/lxc-update/YYYY-MM-DD.log |
Daily log files |
/tmp/lxc-update.lock |
Lock file (auto-released) |
This project is licensed under the MIT License - feel free to use, modify, and distribute.