-
-
Notifications
You must be signed in to change notification settings - Fork 2.1k
CONTRIBUTING
Everything you need to know to contribute to ProxmoxVED
Last Updated: December 2025
Difficulty: Beginner → Advanced
Time to Setup: 15 minutes
Time to Contribute: 1-3 hours
- Quick Start
- Repository Structure
- Development Setup
- Creating New Applications
- Updating Existing Applications
- Code Standards
- Testing Your Changes
- Submitting a Pull Request
- Troubleshooting
- FAQ
# 1. Fork the repository on GitHub
# Visit: https://github.com/community-scripts/ProxmoxVED
# Click: Fork (top right)
# 2. Clone your fork
git clone https://github.com/YOUR_USERNAME/ProxmoxVED.git
cd ProxmoxVED
# 3. Create feature branch
git checkout -b add/my-awesome-app
# 4. Create application scripts
cp ct/example.sh ct/myapp.sh
cp install/example-install.sh install/myapp-install.sh
# 5. Edit your scripts
nano ct/myapp.sh
nano install/myapp-install.sh
# 6. Test locally
bash ct/myapp.sh # Will prompt for container creation
# 7. Commit and push
git add ct/myapp.sh install/myapp-install.sh
git commit -m "feat: add MyApp container"
git push origin add/my-awesome-app
# 8. Open Pull Request on GitHub
# Visit: https://github.com/community-scripts/ProxmoxVED/pulls
# Click: New Pull RequestProxmoxVED/
├── ct/ # 🏗️ Container creation scripts (host-side)
│ ├── pihole.sh
│ ├── docker.sh
│ └── ... (40+ applications)
│
├── install/ # 🛠️ Installation scripts (container-side)
│ ├── pihole-install.sh
│ ├── docker-install.sh
│ └── ... (40+ applications)
│
├── vm/ # 💾 VM creation scripts
│ ├── ubuntu2404-vm.sh
│ ├── debian-vm.sh
│ └── ... (15+ operating systems)
│
├── misc/ # 📦 Shared function libraries
│ ├── build.func # Main orchestrator (3800+ lines)
│ ├── core.func # UI/utilities
│ ├── error_handler.func # Error management
│ ├── tools.func # Tool installation
│ ├── install.func # Container setup
│ ├── cloud-init.func # VM configuration
│ ├── api.func # Telemetry
│ ├── alpine-install.func # Alpine-specific
│ └── alpine-tools.func # Alpine tools
│
├── docs/ # 📚 Documentation
│ ├── UPDATED_APP-ct.md # Container script guide
│ ├── UPDATED_APP-install.md # Install script guide
│ └── CONTRIBUTING.md # (This file!)
│
├── tools/ # 🔧 Proxmox management tools
│ └── pve/
│
└── README.md # Project overview
Container Script: ct/AppName.sh
Installation Script: install/appname-install.sh
Defaults: defaults/appname.vars
Update Script: /usr/bin/update (inside container)
Examples:
ct/pihole.sh → install/pihole-install.sh
ct/docker.sh → install/docker-install.sh
ct/nextcloud-vm.sh → install/nextcloud-vm-install.sh
Rules:
- Container script name: Title Case (PiHole, Docker, NextCloud)
- Install script name: lowercase with hyphens (pihole-install, docker-install)
- Must match:
ct/AppName.sh↔install/appname-install.sh - Directory names: lowercase (always)
- Variable names: lowercase (except APP constant)
-
Proxmox VE 8.0+ with at least:
- 4 CPU cores
- 8 GB RAM
- 50 GB disk space
- Ubuntu 20.04 / Debian 11+ on host
-
Git installed
apt-get install -y git
-
Text Editor (VS Code recommended)
# VS Code extensions: # - Bash IDE # - Shellcheck # - Markdown All in One
# 1. Fork on GitHub (one-time)
# Visit: https://github.com/community-scripts/ProxmoxVED
# Click: Fork
# 2. Clone your fork
git clone https://github.com/YOUR_USERNAME/ProxmoxVED.git
cd ProxmoxVED
# 3. Add upstream remote for updates
git remote add upstream https://github.com/community-scripts/ProxmoxVED.git
# 4. Create feature branch
git checkout -b feat/add-myapp
# 5. Make changes
# ... edit files ...
# 6. Keep fork updated
git fetch upstream
git rebase upstream/main
# 7. Push and open PR
git push origin feat/add-myapp# 1. SSH into Proxmox host
ssh [email protected]
# 2. Download your script
curl -O https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVED/feat/myapp/ct/myapp.sh
# 3. Make it executable
chmod +x myapp.sh
# 4. Update URLs to your fork
# Edit: curl -s https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVED/feat/myapp/...
# 5. Run and test
bash myapp.sh
# 6. If container created successfully, script is working!# You can test script syntax/functionality locally
# Note: Won't fully test (no Proxmox, no actual container)
# Run ShellCheck
shellcheck ct/myapp.sh
shellcheck install/myapp-install.sh
# Syntax check
bash -n ct/myapp.sh
bash -n install/myapp-install.shFor Simple Web Apps (Node.js, Python, PHP):
cp ct/example.sh ct/myapp.sh
cp install/example-install.sh install/myapp-install.shFor Database Apps (PostgreSQL, MongoDB):
cp ct/docker.sh ct/myapp.sh # Use Docker container
# OR manual setup for more controlFor Alpine Linux Apps (lightweight):
# Use ct/alpine.sh as reference
# Edit install script to use Alpine packages (apk not apt)File: ct/myapp.sh
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/YOUR_USERNAME/ProxmoxVED/feat/myapp/misc/build.func)
# Update these:
APP="MyAwesomeApp" # Display name
var_tags="category;tag2;tag3" # Max 3-4 tags
var_cpu="2" # Realistic CPU cores
var_ram="2048" # Min RAM needed (MB)
var_disk="10" # Min disk (GB)
var_os="debian" # OS type
var_version="12" # OS version
var_unprivileged="1" # Security (1=unprivileged)
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/myapp ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
# Get latest version
RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | \
grep "tag_name" | awk '{print substr($2, 2, length($2)-3)}')
if [[ ! -f /opt/${APP}_version.txt ]] || [[ "${RELEASE}" != "$(cat /opt/${APP}_version.txt)" ]]; then
msg_info "Updating ${APP} to v${RELEASE}"
# ... update logic ...
echo "${RELEASE}" > /opt/${APP}_version.txt
msg_ok "Updated ${APP}"
else
msg_ok "No update required. ${APP} is already at v${RELEASE}."
fi
exit
}
start
build_container
description
msg_ok "Completed Successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:PORT${CL}"Checklist:
- APP variable matches filename
- var_tags semicolon-separated (no spaces)
- Realistic CPU/RAM/disk values
- update_script() implemented
- Correct OS and version
- Success message with access URL
File: install/myapp-install.sh
#!/usr/bin/env bash
# Copyright (c) 2021-2025 community-scripts ORG
# Author: YourUsername
# License: MIT
# Source: https://github.com/example/myapp
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt-get install -y \
curl \
wget \
git \
build-essential
msg_ok "Installed Dependencies"
msg_info "Setting up Node.js"
NODE_VERSION="22" setup_nodejs
msg_ok "Node.js installed"
msg_info "Downloading Application"
cd /opt
wget -q "https://github.com/user/repo/releases/download/v1.0.0/myapp.tar.gz"
tar -xzf myapp.tar.gz
rm -f myapp.tar.gz
msg_ok "Application installed"
echo "1.0.0" > /opt/${APP}_version.txt
motd_ssh
customize
cleanup_lxcChecklist:
- Functions loaded from
$FUNCTIONS_FILE_PATH - All installation phases present (deps, tools, app, config, cleanup)
- Using
$STDfor output suppression - Version file saved
- Final cleanup with
cleanup_lxc - No hardcoded versions (use GitHub API)
File: ct/headers/myapp
╔═══════════════════════════════════════╗
║ ║
║ 🎉 MyAwesomeApp 🎉 ║
║ ║
║ Your app is being installed... ║
║ ║
╚═══════════════════════════════════════╝
Save in: ct/headers/myapp (no extension)
File: defaults/myapp.vars
# Default configuration for MyAwesomeApp
var_cpu=4
var_ram=4096
var_disk=15
var_hostname=myapp-container
var_timezone=UTC# Check logs or GitHub releases
curl -fsSL https://api.github.com/repos/app/repo/releases/latest | jq '.'
# Review breaking changes
# Update dependencies if needed# Edit: install/existingapp-install.sh
# 1. Update version (if hardcoded)
RELEASE="2.0.0"
# 2. Update package dependencies (if any changed)
$STD apt-get install -y newdependency
# 3. Update configuration (if format changed)
# Update sed replacements or config files
# 4. Test thoroughly before committing# Edit: ct/existingapp.sh → update_script()
# 1. Update GitHub API URL if repo changed
RELEASE=$(curl -fsSL https://api.github.com/repos/user/repo/releases/latest | ...)
# 2. Update backup/restore logic (if structure changed)
# 3. Update cleanup paths
# 4. Test update on existing installation# Add comment at top of script
# Co-Author: YourUsername
# Updated: YYYY-MM-DD - Description of changes# ✅ Good
APP="MyApp" # Constants (UPPERCASE)
var_cpu="2" # Configuration (var_*)
container_id="100" # Local variables (lowercase)
DB_PASSWORD="secret" # Environment-like (UPPERCASE)
# ❌ Bad
myapp="MyApp" # Inconsistent
VAR_CPU="2" # Wrong convention
containerid="100" # Unclear purpose# ✅ Good
function setup_database() { } # Descriptive
function check_version() { } # Verb-noun pattern
function install_dependencies() { } # Clear action
# ❌ Bad
function setup() { } # Too vague
function db_setup() { } # Inconsistent pattern
function x() { } # Cryptic# ✅ Good
echo "${APP}" # Always quote variables
if [[ "$var" == "value" ]]; then # Use [[ ]] for conditionals
echo "Using $var in string" # Variables in double quotes
# ❌ Bad
echo $APP # Unquoted variables
if [ "$var" = "value" ]; then # Use [[ ]] instead
echo 'Using $var in string' # Single quotes prevent expansion# ✅ Good: Multiline for readability
$STD apt-get install -y \
package1 \
package2 \
package3
# ✅ Good: Complex commands with variables
if ! wget -q "https://example.com/${file}"; then
msg_error "Failed to download"
exit 1
fi
# ❌ Bad: Too long on one line
$STD apt-get install -y package1 package2 package3 package4 package5 package6
# ❌ Bad: No error checking
wget https://example.com/file# ✅ Good: Check critical commands
if ! some_command; then
msg_error "Command failed"
exit 1
fi
# ✅ Good: Use catch_errors for automatic trapping
catch_errors
# ❌ Bad: Silently ignore failures
some_command || true
some_command 2>/dev/null
# ❌ Bad: Unclear what failed
if ! (cmd1 && cmd2 && cmd3); then
msg_error "Something failed"
fi#!/usr/bin/env bash
# Copyright (c) 2021-2025 community-scripts ORG
# Author: YourUsername
# Co-Author: AnotherAuthor (for collaborative work)
# License: MIT | https://github.com/community-scripts/ProxmoxVED/raw/main/LICENSE
# Source: https://github.com/app/repo
# Description: Brief description of what this script does# ✅ Good: Explain WHY, not WHAT
# Use alphanumeric only to avoid shell escaping issues
DB_PASS=$(openssl rand -base64 18 | tr -dc 'a-zA-Z0-9' | head -c13)
# ✅ Good: Comment complex logic
# Detect if running Alpine vs Debian for proper package manager
if grep -qi 'alpine' /etc/os-release; then
PKG_MGR="apk"
else
PKG_MGR="apt"
fi
# ❌ Bad: Comment obvious code
# Set the variable
var="value"
# ❌ Bad: Outdated comments
# TODO: Fix this (written 2 years ago, not fixed)#!/usr/bin/env bash # [1] Shebang (first line)
# Copyright & Metadata # [2] Comments
# [3] Blank line
# Load functions # [4] Import section
source <(curl -fsSL ...)
# [5] Blank line
# Configuration # [6] Variables/Config
APP="MyApp"
var_cpu="2"
# [7] Blank line
# Initialization # [8] Setup
header_info "$APP"
variables
color
catch_errors
# [9] Blank line
# Functions # [10] Function definitions
function update_script() { }
function custom_setup() { }
# [11] Blank line
# Main execution # [12] Script logic
start
build_container# Verify bash syntax
bash -n ct/myapp.sh
bash -n install/myapp-install.sh
# If no output: ✅ Syntax is valid
# If error output: ❌ Fix syntax before submitting# Install ShellCheck
apt-get install -y shellcheck
# Check scripts
shellcheck ct/myapp.sh
shellcheck install/myapp-install.sh
# Review warnings and fix if applicable
# Some warnings can be intentional (use # shellcheck disable=...)# Best: Test on actual Proxmox system
# 1. SSH into Proxmox host
ssh root@YOUR_PROXMOX_IP
# 2. Download your script
curl -O https://raw.githubusercontent.com/YOUR_USER/ProxmoxVED/feat/myapp/ct/myapp.sh
# 3. Make executable
chmod +x myapp.sh
# 4. UPDATE URLS IN SCRIPT to point to your fork
sed -i 's|community-scripts|YOUR_USER|g' myapp.sh
# 5. Run script
bash myapp.sh
# 6. Test interaction:
# - Select installation mode
# - Confirm settings
# - Monitor installation
# 7. Verify container created
pct list | grep myapp
# 8. Log into container and verify app
pct exec 100 bash# Test with different settings:
# Test 1: Advanced (19-step) installation
# When prompted: Select "2" for Advanced
# Test 2: User Defaults
# Before running: Create ~/.community-scripts/default.vars
# When prompted: Select "3" for User Defaults
# Test 3: Error handling
# Simulate network outage (block internet)
# Verify script handles gracefully
# Test 4: Update function
# Create initial container
# Wait for new release
# Run update: bash ct/myapp.sh
# Verify it detects and applies updateBefore submitting PR:
# Code quality
- [ ] Syntax: bash -n passes
- [ ] ShellCheck: No critical warnings
- [ ] Naming: Follows conventions
- [ ] Formatting: Consistent indentation
# Functionality
- [ ] Container creation: Successful
- [ ] Installation: Completes without errors
- [ ] Access URL: Works and app responds
- [ ] Update function: Detects new versions
- [ ] Cleanup: No temporary files left
# Documentation
- [ ] Copyright header present
- [ ] App name matches filenames
- [ ] Default values realistic
- [ ] Success message clear and helpful
# Compatibility
- [ ] Works on Debian 12
- [ ] Works on Ubuntu 22.04
- [ ] (Optional) Works on Alpine 3.20# Update with latest changes
git fetch upstream
git rebase upstream/main
# If conflicts occur:
git rebase --abort
# Resolve conflicts manually then:
git add .
git rebase --continuegit push origin feat/add-myapp
# If already pushed:
git push origin feat/add-myapp --force-with-leaseVisit: https://github.com/community-scripts/ProxmoxVED/pulls
Click: "New Pull Request"
Select: community-scripts:main ← YOUR_USERNAME:feat/myapp
Use this template:
## Description
Brief description of what this PR adds/fixes
## Type of Change
- [ ] New application (ct/AppName.sh + install/appname-install.sh)
- [ ] Update existing application
- [ ] Bug fix
- [ ] Documentation update
- [ ] Other: _______
## Testing
- [ ] Tested on Proxmox VE 8.x
- [ ] Container creation successful
- [ ] Application installation successful
- [ ] Application is accessible at URL
- [ ] Update function works (if applicable)
- [ ] No temporary files left after installation
## Application Details (for new apps only)
- **App Name**: MyApp
- **Source**: https://github.com/app/repo
- **Default OS**: Debian 12
- **Recommended Resources**: 2 CPU, 2GB RAM, 10GB Disk
- **Tags**: category;tag2;tag3
- **Access URL**: http://IP:PORT/path
## Checklist
- [ ] My code follows the style guidelines
- [ ] I have performed a self-review
- [ ] I have tested the script locally
- [ ] ShellCheck shows no critical warnings
- [ ] Documentation is accurate and complete
- [ ] I have added/updated relevant documentationMaintainers may request changes:
- Fix syntax/style issues
- Add better error handling
- Optimize resource usage
- Update documentation
To address feedback:
# Make requested changes
git add .
git commit -m "Address review feedback: ..."
git push origin feat/add-myapp
# PR automatically updates!
# No need to create new PROnce merged, your contribution will be part of ProxmoxVED and available to all users!
# Check your fork exists
# Visit: https://github.com/YOUR_USERNAME/ProxmoxVED
# If not there: Click "Fork" on original repo first# Setup SSH key
ssh-keygen -t ed25519 -C "[email protected]"
cat ~/.ssh/id_ed25519.pub # Copy this
# Add to GitHub: Settings → SSH Keys → New Key
# Or use HTTPS with token:
git remote set-url origin https://[email protected]/YOUR_USERNAME/ProxmoxVED.git# Use ShellCheck to identify issues
shellcheck install/myapp-install.sh
# Common issues:
# - Unmatched quotes: "string' or 'string"
# - Missing semicolons before then: if [...]; then
# - Wrong quoting: echo $VAR instead of echo "${VAR}"# 1. Check Proxmox resources
free -h # Check RAM
df -h # Check disk space
pct list # Check CTID availability
# 2. Check script URL
# Make sure curl -s in script points to your fork
# 3. Review errors
# Run with verbose: bash -x ct/myapp.sh# 1. Verify container running
pct list
pct status CTID
# 2. Check if service running inside
pct exec CTID systemctl status myapp
# 3. Check firewall
# Proxmox host: iptables -L
# Container: iptables -L
# 4. Verify listening port
pct exec CTID netstat -tlnp | grep LISTENA: No! The codebase has many examples you can copy. Most contributions are straightforward script creation following the established patterns.
A: No. ProxmoxVED focuses on open-source applications (GPL, MIT, Apache, etc.). Closed-source applications won't be accepted.
A: Maintainers are volunteers. Reviews typically happen within 1-2 weeks. Complex changes may take longer.
A: Partially. You can verify syntax and ShellCheck compliance locally, but real container testing requires Proxmox. Consider using:
- Proxmox in a VM (VirtualBox/KVM)
- Test instances on Hetzner/DigitalOcean
- Ask maintainers to test for you
A: Yes! Update functions can be complex if needed. Just ensure:
- Backup user data before updating
- Restore user data after update
- Test thoroughly before submitting
- Add clear comments explaining logic
A: Generally no. build.func is the orchestrator and should remain stable. New functions should go in:
-
tools.func- Tool installation -
core.func- Utility functions -
install.func- Container setup
Ask in an issue first if you're unsure.
A: You have options:
Option 1: Use Advanced mode (19-step wizard)
# Extend advanced_settings() if app needs special varsOption 2: Create custom setup menu
function custom_config() {
OPTION=$(whiptail --inputbox "Enter database name:" 8 60)
# ... use $OPTION in installation
}Option 3: Leave as defaults + documentation
# In success message:
echo "Edit /opt/myapp/config.json to customize settings"A:
- Windows: Not planned (ProxmoxVED is Linux/Proxmox focused)
- macOS: Can contribute Docker-based alternatives
- ARM: Yes! Many apps work on ARM. Add to vm/pimox-*.sh scripts
-
Documentation:
/docsdirectory and wikis -
Function Reference:
/misc/*.mdwiki files -
Examples: Look at similar applications in
/ctand/install - GitHub Issues: https://github.com/community-scripts/ProxmoxVED/issues
- Discussions: https://github.com/community-scripts/ProxmoxVED/discussions
- Check existing issues - Your question may be answered
-
Search documentation - See
/docsand/misc/*.md - Ask in Discussions - For general questions
- Open an Issue - For bugs or specific problems
When reporting bugs, include:
- Which application
- What happened (error message)
- What you expected
- Your Proxmox version
- Container OS and version
Example:
Title: pihole-install.sh fails on Alpine 3.20
Description:
Installation fails with error: "PHP-FPM not found"
Expected:
PiHole should install successfully
Environment:
- Proxmox VE 8.2
- Alpine 3.20
- Container CTID 110
Error Output:
[ERROR] in line 42: exit code 127: while executing command php-fpm --start
ProxmoxVED by the Numbers:
- 🎯 40+ applications supported
- 👥 100+ contributors
- 📊 10,000+ GitHub stars
- 🚀 50+ releases
- 📈 100,000+ downloads/month
Your contribution makes a difference!
By contributing, you agree to:
- ✅ Be respectful and inclusive
- ✅ Follow the style guidelines
- ✅ Test your changes thoroughly
- ✅ Provide clear commit messages
- ✅ Respond to review feedback
Ready to contribute? Start with the Quick Start section!
Questions? Open an issue or start a discussion on GitHub.
Thank you for your contribution! 🙏