Skip to content

Instantly share code, notes, and snippets.

@afkarxyz
Last active June 11, 2026 23:00
Show Gist options
  • Select an option

  • Save afkarxyz/fcb0977530ebc1f9c819162ec3d8b120 to your computer and use it in GitHub Desktop.

Select an option

Save afkarxyz/fcb0977530ebc1f9c819162ec3d8b120 to your computer and use it in GitHub Desktop.
Easypanel Quick Setup
#!/bin/bash
###############################################################################
# Easypanel Quick Setup
# Author: afkarxyz
###############################################################################
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
PATCH_JS_TARGET="/etc/easypanel/patch-backend.js"
PATCH_WRAPPER="/etc/easypanel/patch.sh"
PATCH_WATCHER="/root/patch-easypanel.sh"
PATCH_SERVICE_UNIT="/etc/systemd/system/patch-easypanel.service"
PATCH_TIMER_UNIT="/etc/systemd/system/patch-easypanel-timer.service"
PATCH_TIMER="/etc/systemd/system/patch-easypanel.timer"
PATCH_LOG="/var/log/easypanel-patch.log"
LEGACY_PATCH_DIR="/root/easypanel-patch"
LEGACY_REPATCH_SCRIPT="$LEGACY_PATCH_DIR/repatch.sh"
LEGACY_CUSTOM_IMAGE="easypanel/easypanel:patched"
ORIGINAL_IMAGE="easypanel/easypanel:latest"
get_service() {
docker service ls --filter "name=easypanel" --format "{{.Name}}" | head -1
}
confirm() {
local reply
read -p "Continue? [Y/n]: " -n 1 -r reply < /dev/tty
echo
[[ -z "$reply" || "$reply" =~ ^[Yy]$ ]]
}
ensure_docker() {
if command -v docker > /dev/null 2>&1; then
echo -e "${GREEN}✓ Docker detected${NC}"
return 0
fi
echo -e "${YELLOW}Docker not found. Installing...${NC}"
curl -sSL https://get.docker.com | sh
if ! command -v docker > /dev/null 2>&1; then
echo -e "${RED}ERROR: Docker installation failed${NC}"
return 1
fi
systemctl enable --now docker > /dev/null 2>&1 || true
echo -e "${GREEN}✓ Docker installed${NC}"
return 0
}
ensure_easypanel() {
if [ -n "$(get_service)" ]; then
echo -e "${GREEN}✓ Easypanel service detected${NC}"
return 0
fi
echo -e "${YELLOW}Easypanel not found. Installing...${NC}"
docker run --rm -it \
-v /etc/easypanel:/etc/easypanel \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
easypanel/easypanel setup
if [ -z "$(get_service)" ]; then
echo -e "${RED}ERROR: Easypanel installation failed${NC}"
return 1
fi
echo -e "${GREEN}✓ Easypanel installed${NC}"
return 0
}
write_patch_backend() {
mkdir -p /etc/easypanel
cat > "$PATCH_JS_TARGET" << 'PATCHJS'
const fs = require("fs");
let code = fs.readFileSync("/app/backend.js", "utf-8");
let patches = 0;
const fake = '({valid:!0,meta:{product_id:108143},license_key:"patched"})';
const portalFake = '({plan:{options:{advanced_monitoring:!0,notifications:!0,branding:!0,mutiple_users:!0,access_control:!0,cluster:!0}}})';
const projectLimit = "return e?.valid||t?!0:r.length<3}";
if (code.includes(projectLimit)) {
code = code.replace(projectLimit, "return!0}");
patches++;
console.log("[patch] Removed project limit");
}
const lemonInitRegex = /global\.lemonLicenseManager=(\w+)/;
const lemonMatch = code.match(lemonInitRegex);
if (lemonMatch) {
const varName = lemonMatch[1];
code = code.replace(
lemonMatch[0],
`global.lemonLicenseManager=${varName};${varName}.licensePayload=${fake}`
);
patches++;
console.log("[patch] Injected fake licensePayload on lemonLicenseManager (var: " + varName + ")");
}
const lemonHandler = "({...await ng(),license_key:void 0})";
if (code.includes(lemonHandler)) {
code = code.replace(lemonHandler, fake);
patches++;
console.log("[patch] Faked lemon tRPC getLicensePayload response");
}
const portalHandler = "getLicensePayload:xt.query(async()=>br())";
if (code.includes(portalHandler)) {
code = code.replace(portalHandler, "getLicensePayload:xt.query(async()=>" + portalFake + ")");
patches++;
console.log("[patch] Faked portal tRPC getLicensePayload response");
}
const whitelabelThrow = 'throw new se({code:"BAD_REQUEST",message:"You need a license that supports white-labeling."})';
if (code.includes(whitelabelThrow)) {
const count = code.split(whitelabelThrow).length - 1;
code = code.replaceAll(whitelabelThrow, "void 0");
patches++;
console.log("[patch] Removed " + count + " white-labeling license checks");
}
const customDomainThrow = 'throw new se({code:"BAD_REQUEST",message:"Custom service domain feature requires a valid license"})';
if (code.includes(customDomainThrow)) {
code = code.replaceAll(customDomainThrow, "void 0");
patches++;
console.log("[patch] Removed custom service domain license check");
}
fs.writeFileSync("/app/backend.js", code);
console.log("[patch] Done. Applied " + patches + " patches.");
PATCHJS
}
write_patch_wrapper() {
cat > "$PATCH_WRAPPER" << 'EOF'
#!/bin/sh
node /etc/easypanel/patch-backend.js
exec node backend.js "$@"
EOF
chmod +x "$PATCH_WRAPPER"
}
write_patch_watcher() {
cat > "$PATCH_WATCHER" << 'WATCHER'
#!/bin/bash
LOG="/var/log/easypanel-patch.log"
PATCH_JS="/etc/easypanel/patch-backend.js"
PATCH_WRAPPER="/etc/easypanel/patch.sh"
get_service() {
docker service ls --filter "name=easypanel" --format "{{.Name}}" | head -1
}
SERVICE=$(get_service)
if [ -z "$SERVICE" ]; then
echo "[$(date)] Service not found, skipping." >> "$LOG"
exit 0
fi
if [ ! -f "$PATCH_JS" ] || [ ! -f "$PATCH_WRAPPER" ]; then
echo "[$(date)] Patch files missing, cannot re-apply. Re-run setup script." >> "$LOG"
exit 1
fi
CURRENT=$(docker service inspect "$SERVICE" --format '{{json .Spec.TaskTemplate.ContainerSpec.Command}} {{json .Spec.TaskTemplate.ContainerSpec.Args}}' 2>/dev/null)
if echo "$CURRENT" | grep -q "patch.sh"; then
echo "[$(date)] Entrypoint already patched." >> "$LOG"
exit 0
fi
echo "[$(date)] Entrypoint not patched (current: $CURRENT). Re-applying..." >> "$LOG"
docker service update "$SERVICE" \
--image "easypanel/easypanel:latest" \
--entrypoint "/etc/easypanel/patch.sh" \
--args "start" \
--force >> "$LOG" 2>&1
echo "[$(date)] Re-apply exit code: $?" >> "$LOG"
WATCHER
chmod +x "$PATCH_WATCHER"
}
setup_patch_service() {
write_patch_watcher
cat > "$PATCH_SERVICE_UNIT" << 'EOF'
[Unit]
Description=Auto-patch Easypanel entrypoint on container start
After=docker.service
Requires=docker.service
[Service]
Type=simple
ExecStart=/bin/bash -c 'docker events --filter "image=easypanel/easypanel" --filter "event=start" --format "{{.Actor.ID}}" | while read id; do sleep 10 && /root/patch-easypanel.sh; done'
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
cat > "$PATCH_TIMER_UNIT" << 'EOF'
[Unit]
Description=Periodic Easypanel patch check
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
ExecStart=/root/patch-easypanel.sh
EOF
cat > "$PATCH_TIMER" << 'EOF'
[Unit]
Description=Run Easypanel patch check every 5 minutes
[Timer]
OnBootSec=2min
OnUnitActiveSec=5min
Unit=patch-easypanel-timer.service
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable patch-easypanel > /dev/null 2>&1
systemctl restart patch-easypanel
systemctl enable patch-easypanel.timer > /dev/null 2>&1
systemctl restart patch-easypanel.timer
}
remove_patch_service() {
systemctl stop patch-easypanel > /dev/null 2>&1 || true
systemctl disable patch-easypanel > /dev/null 2>&1 || true
systemctl stop patch-easypanel.timer > /dev/null 2>&1 || true
systemctl disable patch-easypanel.timer > /dev/null 2>&1 || true
rm -f "$PATCH_SERVICE_UNIT"
rm -f "$PATCH_TIMER_UNIT"
rm -f "$PATCH_TIMER"
rm -f "$PATCH_WATCHER"
systemctl daemon-reload
}
remove_legacy_patch() {
( crontab -l 2>/dev/null | grep -v "easypanel-repatch\|repatch.sh" ) | crontab - 2>/dev/null || true
rm -f "$LEGACY_REPATCH_SCRIPT"
docker rmi "$LEGACY_CUSTOM_IMAGE" > /dev/null 2>&1 || true
}
apply_patch_to_service() {
local service
service=$(get_service)
if [ -z "$service" ]; then
echo -e "${RED}ERROR: Service not found${NC}"
return 1
fi
if ! docker service update "$service" \
--image "$ORIGINAL_IMAGE" \
--entrypoint "/etc/easypanel/patch.sh" \
--args "start" \
--force > /dev/null 2>&1; then
echo -e "${RED}ERROR: Update failed${NC}"
return 1
fi
return 0
}
is_patched() {
local service
local current
service=$(get_service)
if [ -n "$service" ]; then
current=$(docker service inspect "$service" --format '{{json .Spec.TaskTemplate.ContainerSpec.Command}} {{json .Spec.TaskTemplate.ContainerSpec.Args}}' 2>/dev/null)
if echo "$current" | grep -q "patch.sh"; then
return 0
fi
fi
[ -f "$PATCH_JS_TARGET" ] || [ -f "$PATCH_WRAPPER" ] || [ -f "$PATCH_SERVICE_UNIT" ] || [ -f "$PATCH_WATCHER" ]
}
setup_server() {
echo -e "${BLUE}========================================================${NC}"
echo -e "${BLUE} Easypanel Quick Setup${NC}"
echo -e "${BLUE} by afkarxyz${NC}"
echo -e "${BLUE}========================================================${NC}"
echo ""
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}ERROR: Run as root${NC}"
exit 1
fi
if ! confirm; then
exit 0
fi
echo ""
echo -e "${YELLOW}[1/5]${NC} Updating system packages..."
apt update && apt upgrade -y
echo -e "${GREEN}✓ System updated${NC}"
echo ""
echo -e "${YELLOW}[2/5]${NC} Configuring firewall..."
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable
echo -e "${GREEN}✓ Firewall configured (port 22, 80, 443)${NC}"
echo ""
echo -e "${YELLOW}[3/5]${NC} Checking Docker..."
if ! ensure_docker; then
exit 1
fi
echo ""
echo -e "${YELLOW}[4/5]${NC} Checking Easypanel..."
if ! ensure_easypanel; then
exit 1
fi
echo ""
echo -e "${YELLOW}[5/5]${NC} Setting up Docker prune cronjob..."
CRON_JOB="0 * * * * docker builder prune -af && docker container prune -f >> /var/log/docker-prune.log 2>&1"
( crontab -l 2>/dev/null | grep -v "docker builder prune"; echo "$CRON_JOB" ) | crontab -
echo -e "${GREEN}✓ Cronjob configured (every hour)${NC}"
echo ""
echo -e "${GREEN}========================================================${NC}"
echo -e "${GREEN} Setup Complete!${NC}"
echo -e "${GREEN}========================================================${NC}"
echo ""
echo -e " ${GREEN}✓${NC} System updated"
echo -e " ${GREEN}✓${NC} Firewall: port 22, 80, 443 allowed"
echo -e " ${GREEN}✓${NC} Docker ready"
echo -e " ${GREEN}✓${NC} Easypanel service ready"
echo -e " ${GREEN}✓${NC} Docker prune cronjob (every hour)"
echo ""
}
patch_easypanel() {
echo -e "${GREEN}========================================================${NC}"
echo -e "${GREEN} Easypanel Quick Setup - PATCH${NC}"
echo -e "${GREEN}========================================================${NC}"
echo ""
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}ERROR: Run as root${NC}"
exit 1
fi
if ! ensure_docker; then
exit 1
fi
if ! ensure_easypanel; then
exit 1
fi
if ! confirm; then
exit 0
fi
echo -e "\n${YELLOW}[1/4]${NC} Preparing backend patch files..."
write_patch_backend
write_patch_wrapper
echo -e "${GREEN}✓ Patch files ready${NC}"
echo -e "${YELLOW}[2/4]${NC} Cleaning up legacy patch method..."
remove_legacy_patch
echo -e "${GREEN}✓ Legacy cron/image cleanup complete${NC}"
echo -e "${YELLOW}[3/4]${NC} Enabling auto re-patch service..."
if ! setup_patch_service; then
echo -e "${RED}ERROR: Failed to enable patch service${NC}"
exit 1
fi
echo -e "${GREEN}✓ Auto re-patch enabled via systemd${NC}"
echo -e "${YELLOW}[4/4]${NC} Applying backend patch..."
if ! apply_patch_to_service; then
exit 1
fi
echo -e "${GREEN}✓ Backend patch applied${NC}"
echo -e "${YELLOW}Done.${NC}"
echo ""
echo -e "${GREEN}========================================================${NC}"
echo -e "${GREEN} Patch Applied${NC}"
echo -e "${GREEN}========================================================${NC}"
echo ""
echo -e " ${GREEN}✓${NC} Backend patch applied"
echo -e " ${GREEN}✓${NC} Auto re-patch active via systemd watcher"
echo -e " ${GREEN}✓${NC} Log: ${YELLOW}tail -f $PATCH_LOG${NC}"
echo ""
echo -e "${YELLOW}Clear browser cache (Ctrl+Shift+R)${NC}"
echo ""
}
unpatch_easypanel() {
echo -e "${RED}========================================================${NC}"
echo -e "${RED} Easypanel Quick Setup - UNPATCH${NC}"
echo -e "${RED}========================================================${NC}"
echo ""
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}ERROR: Run as root${NC}"
exit 1
fi
if ! is_patched; then
echo -e "${YELLOW}Nothing to unpatch${NC}"
exit 0
fi
if ! confirm; then
exit 0
fi
echo -e "\n${YELLOW}[1/4]${NC} Finding service..."
SERVICE=$(get_service)
if [ -z "$SERVICE" ]; then
echo -e "${RED}ERROR: Service not found${NC}"
exit 1
fi
echo -e "${YELLOW}[2/4]${NC} Restoring original image..."
if ! docker service update "$SERVICE" \
--image "$ORIGINAL_IMAGE" \
--entrypoint "node" \
--args "backend.js start" \
--force > /dev/null 2>&1; then
echo -e "${RED}ERROR: Restore failed${NC}"
exit 1
fi
echo -e "${YELLOW}[3/4]${NC} Removing auto re-patch..."
remove_patch_service
echo -e "${GREEN}✓ Auto re-patch disabled${NC}"
echo -e "${YELLOW}[4/4]${NC} Cleaning up..."
rm -f "$PATCH_JS_TARGET" "$PATCH_WRAPPER"
remove_legacy_patch
echo ""
echo -e "${GREEN}========================================================${NC}"
echo -e "${GREEN} Unpatch Complete${NC}"
echo -e "${GREEN}========================================================${NC}"
echo ""
}
show_menu() {
while true; do
echo -e "${BLUE}========================================================${NC}"
echo -e "${BLUE} Easypanel Quick Setup${NC}"
echo -e "${BLUE} by afkarxyz${NC}"
echo -e "${BLUE}========================================================${NC}"
echo ""
echo -e " ${GREEN}1)${NC} Prepare Server + Patch"
echo -e " ${GREEN}2)${NC} Patch Only"
echo -e " ${RED}3)${NC} Unpatch"
echo -e " ${YELLOW}4)${NC} Exit"
echo ""
read -p "Choice [1-4]: " choice < /dev/tty
echo ""
case $choice in
1) setup_server; patch_easypanel; break ;;
2) patch_easypanel; break ;;
3) unpatch_easypanel; break ;;
4) exit 0 ;;
*) echo -e "${RED}Invalid${NC}\n" ;;
esac
done
}
if [ $# -eq 0 ]; then
show_menu
else
case "$1" in
setup|--setup|-s) setup_server ;;
patch|--patch|-p) patch_easypanel ;;
unpatch|--unpatch|-u) unpatch_easypanel ;;
all|--all|-a) setup_server; patch_easypanel ;;
--help|-h)
echo "Easypanel Quick Setup by afkarxyz"
echo ""
echo "Usage:"
echo " $0 Interactive menu"
echo " $0 setup Prepare server only"
echo " $0 patch Apply patch only"
echo " $0 unpatch Remove patch"
echo " $0 all Prepare server + patch"
exit 0
;;
*)
echo -e "${RED}Invalid option${NC}"
exit 1
;;
esac
fi
@afkarxyz

afkarxyz commented Feb 6, 2026

Copy link
Copy Markdown
Author
curl -fsSL https://gist.githubusercontent.com/afkarxyz/fcb0977530ebc1f9c819162ec3d8b120/raw/easypanel-quick-setup.sh -o /tmp/easypanel-quick-setup.sh && sudo bash /tmp/easypanel-quick-setup.sh

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment