- Shell 95.6%
- Python 4.4%
Patterns: - One-time HTTP serve setup per bridge container (busybox httpd on :9876) - Init container fetches registration.yaml over LAN on pod start - List-driven templating pattern for scalable bridge addition - Verification commands + 5 common pitfalls Pairs with the 14 ct/*.sh bridge deploy scripts from 9ed2efe. |
||
|---|---|---|
| .github/workflows | ||
| ct | ||
| docs | ||
| images | ||
| install | ||
| misc | ||
| profiles | ||
| scripts | ||
| tools | ||
| .gitignore | ||
| colima-Darwin-arm64 | ||
| common.sh | ||
| CONTRIBUTING.md | ||
| debug-storage.sh | ||
| fix-compat-local.sh | ||
| fix-master.sh | ||
| gen_all.sh | ||
| gen_docs.sh | ||
| LICENSE | ||
| README.md | ||
Incus Helper Scripts
One-command application deployment for Incus containers
Quick Start • Templates • Usage • Development
Inspired by the Proxmox VE Community Helper Scripts, this project provides ready-to-run scripts that deploy applications inside Incus containers with zero configuration.
Each application has two scripts:
ct/<app>.sh— runs on the Incus host, creates the container with appropriate resources, then triggers the installinstall/<app>-install.sh— runs inside the container, installs the application and configures it
✨ Quick Start
# Deploy Nginx in 10 seconds
bash <(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/ct/nginx.sh)
# Deploy PostgreSQL
bash <(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/ct/postgresql.sh)
# Deploy Ollama (LLM inference)
bash <(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/ct/ollama.sh)
Prerequisites: Incus 6.0+ installed and initialized.
📦 Available Templates
| Category | Application | Port | Resources |
|---|---|---|---|
| Reverse Proxy | Nginx | 80 | 1CPU / 512MB / 4GB |
| Nginx Proxy Manager | 81 | 1CPU / 1GB / 6GB | |
| Traefik | 8080 | 1CPU / 512MB / 4GB | |
| Database | PostgreSQL 16 | 5432 | 2CPU / 1GB / 8GB |
| Redis | 6379 | 1CPU / 512MB / 4GB | |
| MinIO (S3) | 9001 | 2CPU / 2GB / 20GB | |
| AI / LLM | Ollama | 11434 | 4CPU / 8GB / 30GB |
| Media | Jellyfin | 8096 | 2CPU / 2GB / 20GB |
| Immich | 2283 | 4CPU / 4GB / 50GB | |
| Cloud | Nextcloud | 80 | 2CPU / 2GB / 20GB |
| Password Manager | Vaultwarden | 80 | 1CPU / 1GB / 8GB |
| Smart Home | Home Assistant | 8123 | 2CPU / 2GB / 20GB |
| DNS | AdGuard Home | 80 | 1CPU / 512MB / 4GB |
| Dashboard | Homarr | 7575 | 1CPU / 1GB / 6GB |
| Monitoring | Uptime Kuma | 3001 | 1CPU / 1GB / 6GB |
📋 View full variable reference
Each script accepts these environment variables to override defaults:
| Variable | Default | Description |
|---|---|---|
var_cpu |
per-app | CPU cores (e.g., 2) |
var_ram |
per-app | RAM in MB (e.g., 2048) |
var_disk |
per-app | Disk in GB (e.g., 10) |
var_os |
ubuntu |
OS image (e.g., alpine, debian) |
var_version |
24.04 |
OS version (e.g., 12 for Debian) |
var_instance |
app name | Instance name override |
var_ipv4 |
auto | Static IPv4 address (e.g., 10.0.0.50) |
var_profile |
default |
Incus profile to use |
var_tags |
per-app | Comma-separated tags for metadata |
var_unprivileged |
true |
Run unprivileged container |
var_type |
container |
container or virtual-machine |
var_image |
auto | Full image alias override |
var_storage |
auto | Storage pool name |
var_network |
auto | Network bridge name |
Example with overrides:
var_cpu=4 var_ram=4096 var_disk=50 var_ipv4=10.0.0.50 \
bash <(curl -fsSL https://.../ct/postgresql.sh)
🚀 Usage
Deploy an application
# Use the defaults
bash <(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/ct/nginx.sh)
# With custom resources
var_cpu=2 var_ram=2048 var_disk=20 \
bash <(curl -fsSL https://...<app>.sh)
Run install in an existing instance
incus exec <instance> -- bash -c "$(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/install/<app>-install.sh)"
Manage instances
# List all instances
bash <(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/tools/incus-list.sh)
# Backup an instance
bash <(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/tools/incus-backup.sh) my-instance
# Backup all instances
bash <(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/tools/incus-backup.sh) --all
# Update all instances
bash <(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/tools/incus-update-all.sh)
Profiles
Pre-defined profiles make it easy to standardize resources across instances:
# Load the app profile
incus profile create app
incus profile edit app < profiles/profile-templates.yaml
incus launch images:ubuntu/24.04 my-app --profile default --profile app
🏗 Project Structure
incus-helper-scripts/
├── ct/ # Container creation scripts (run on host)
│ ├── nginx.sh
│ ├── postgresql.sh
│ └── ...
├── install/ # In-container install scripts
│ ├── nginx-install.sh
│ ├── postgresql-install.sh
│ └── ...
├── misc/ # Shared framework
│ ├── incus-build.func # Container creation functions
│ └── incus-install.func # In-container installation helpers
├── tools/ # Utility scripts
│ ├── incus-list.sh
│ ├── incus-backup.sh
│ └── incus-update-all.sh
├── profiles/ # Incus profile templates
│ └── profile-templates.yaml
├── images/ # Image reference docs
│ └── incus-image-aliases.md
├── common.sh # Shared utilities (colors, logging, helpers)
├── CONTRIBUTING.md
├── LICENSE
└── README.md
🛠 Development
Adding a new template
Each app needs two files:
ct/<app>.sh— Host-side script that creates the container:
#!/usr/bin/env bash
source /dev/stdin <<<"$(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/common.sh)"
source /dev/stdin <<<"$(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/misc/incus-build.func)"
APP="My App"
var_tags="${var_tags:-tag1,tag2}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-1024}"
var_disk="${var_disk:-10}"
header_info "$APP"
variables
check_existing_instance
create_instance
INSTALL_URL="https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/install/myapp-install.sh"
incus_exec "$var_instance" -- bash -c "$(curl -fsSL "$INSTALL_URL")"
IP=$(get_instance_ip "$var_instance")
echo "Access: http://${IP}:<port>"
install/<app>-install.sh— In-container install script:
#!/usr/bin/env bash
source /dev/stdin <<<"$(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/common.sh)"
source /dev/stdin <<<"$(curl -fsSL https://codeberg.org/luna-dj/incus-scripts/raw/branch/main/misc/incus-install.func)"
header_info "My App"
setting_up_container
network_check
configure_apt
# Install your app here...
print_completion "My App" "$IP" "<port>"
Running locally (without the repo)
# Clone the repo
git clone https://codeberg.org/luna-dj/incus-scripts
cd incus-helper-scripts
# Run a script directly
bash ct/nginx.sh
# Or source the functions
source common.sh
source misc/incus-build.func
# ... use functions directly
📚 Advanced
Networking
Each container gets an IP via DHCP by default. For a static IP:
var_ipv4="10.0.0.50" bash ct/postgresql.sh
Proxy devices forward host ports to container ports:
incus config device add <instance> proxy-8080 proxy \
listen=tcp:0.0.0.0:8080 connect=tcp:127.0.0.1:80
Storage
Mount additional storage:
incus config device add <instance> media disk \
source=/mnt/media path=/media
Updates
Re-running a ct script on an existing instance triggers the update_script function (if implemented). Otherwise:
# Re-run just the install part
incus exec <instance> -- bash -c "$(curl -fsSL https://.../install/<app>-install.sh)"
🧪 Testing
# Check all scripts have the right header
grep -l "^#!/usr/bin/env bash" ct/*.sh | wc -l
# Shellcheck validation
shellcheck ct/*.sh install/*.sh misc/*.func tools/*.sh
# Check for common variables
grep "var_cpu=" ct/*.sh | wc -l
🤝 Contributing
We welcome contributions! See CONTRIBUTING.md for guidelines.
Auto-syncing from upstream
The ct/ and install/ scripts for almost all 600+ apps are automatically regenerated from
community-scripts/ProxmoxVE:
- Schedule: Daily at 06:00 UTC (
.github/workflows/regen.yml) - Manual: Run from the Actions tab → "Regen from upstream" → "Run workflow"
- Script:
scripts/regen-from-upstream.py - Output: Pull request with all changes; merges to
mainmirror to Codeberg
Apps with hand-written fixes (e.g. mail-archiver arm64 .NET SDK workaround) are listed in
CUSTOM_INSTALL_APPS in scripts/regen-from-upstream.py and never overwritten by the regen.
To regenerate locally:
python3 scripts/regen-from-upstream.py
📄 License
MIT — see LICENSE.
🙏 Acknowledgements
- Proxmox VE Helper Scripts for the inspiration and structure
- Linux Containers for Incus
- All template authors and contributors