dokploy/packages/server/src/setup/server-setup.ts
Narciso bee6918d9a fix(monitoring): remove legacy container and default empty cronJob
The swarm migration in 3848fa9c0 dropped the container.remove({force:true})
that the standalone deploy path used to run. Swarm tasks are named
dokploy-monitoring.<slot>.<id>, so there is no name collision and the
pre-v0.30.0 container survives every redeploy. It also stays pinned to an
orphaned image ID once pullRemoteImage moves the latest tag, so neither a
pull nor a Save clears it and it restarts forever.

Cloud setup spread metricsConfig straight from the row, shipping
cronJob: "" to the agent. robfig/cron rejects an empty spec, so the Go
binary exits before Fiber binds 4500 and Docker restarts it every ~60s.

- remove the legacy container in deployMonitoringService, which covers both
  setupMonitoring and setupWebMonitoring. Cleanup is best effort: a failure
  is logged and the deploy continues, matching the pre-migration behaviour
- default cronJob when configuring monitoring for cloud
- on build servers, clean up the legacy container but deploy no service.
  They never join the swarm, yet cloud setup did create the standalone
  container there before v0.30.0, and the monitoring form has always been
  hidden for them, so those agents are all stuck with an empty cronJob
- cover the above with real-docker tests
2026-08-20 11:17:41 -04:00

788 lines
25 KiB
TypeScript

import path from "node:path";
import { IS_CLOUD, paths } from "@dokploy/server/constants";
import { getDokployUrl } from "@dokploy/server/services/admin";
import {
createServerDeployment,
updateDeploymentStatus,
} from "@dokploy/server/services/deployment";
import {
findServerById,
updateServerById,
} from "@dokploy/server/services/server";
import {
getDefaultMiddlewares,
getDefaultServerTraefikConfig,
TRAEFIK_HTTP3_PORT,
TRAEFIK_PORT,
TRAEFIK_SSL_PORT,
TRAEFIK_VERSION,
} from "@dokploy/server/setup/traefik-setup";
import slug from "slugify";
import { Client } from "ssh2";
import { recreateDirectory } from "../utils/filesystem/directory";
import { setupMonitoring } from "./monitoring-setup";
const generateToken = () => {
const array = new Uint8Array(64);
crypto.getRandomValues(array);
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join(
"",
);
};
export const slugify = (text: string | undefined) => {
if (!text) {
return "";
}
const cleanedText = text.trim().replace(/[^a-zA-Z0-9\s]/g, "");
return slug(cleanedText, {
lower: true,
trim: true,
strict: true,
});
};
export const serverSetup = async (
serverId: string,
onData?: (data: any) => void,
) => {
const server = await findServerById(serverId);
const { LOGS_PATH } = paths();
const slugifyName = slugify(`server ${server.name}`);
const fullPath = path.join(LOGS_PATH, slugifyName);
await recreateDirectory(fullPath);
const deployment = await createServerDeployment({
serverId: server.serverId,
title: "Setup Server",
description: "Setup Server",
});
try {
const isBuildServer = server.serverType === "build";
onData?.(
isBuildServer
? "\nInstalling Build Server Dependencies: ✅\n"
: "\nInstalling Server Dependencies: ✅\n",
);
await installRequirements(serverId, onData);
if (IS_CLOUD) {
onData?.("\nConfiguring Monitoring: 🔄\n");
const baseUrl = await getDokployUrl();
const token = generateToken();
const urlCallback = `${baseUrl}/api/trpc/notification.receiveNotification`;
// Update server with monitoring configuration
await updateServerById(serverId, {
metricsConfig: {
server: {
...server.metricsConfig.server,
token: token,
urlCallback: urlCallback,
cronJob: server.metricsConfig.server.cronJob || "0 0 * * *",
},
containers: server.metricsConfig.containers,
},
});
await setupMonitoring(serverId);
onData?.("\nMonitoring Configured: ✅\n");
}
await updateDeploymentStatus(deployment.deploymentId, "done");
onData?.("\nSetup Server: ✅\n");
} catch (err) {
console.log(err);
await updateDeploymentStatus(deployment.deploymentId, "error");
onData?.(`${err}\n`);
}
};
export const reportDockerVersion = () => `
if command -v docker >/dev/null 2>&1; then
INSTALLED_DOCKER_VERSION=$(docker version --format '{{.Server.Version}}' 2>/dev/null || true)
if [ -z "$INSTALLED_DOCKER_VERSION" ]; then
INSTALLED_DOCKER_VERSION=$(docker --version 2>/dev/null | awk '{print $3}' | tr -d ',' || true)
fi
if [ -z "$INSTALLED_DOCKER_VERSION" ]; then
INSTALLED_DOCKER_VERSION="unknown"
fi
DOCKER_VERSION_REPORT="$INSTALLED_DOCKER_VERSION (already installed)"
else
DOCKER_VERSION_REPORT="$DOCKER_VERSION (will be installed)"
fi
`;
export const defaultCommand = (isBuildServer = false) => {
const bashCommand = `
set -e;
DOCKER_VERSION=28.5.0
OS_TYPE=$(grep -w "ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"')
SYS_ARCH=$(uname -m)
CURRENT_USER=$USER
echo "Installing requirements for: OS: $OS_TYPE"
# Auto-detect sudo requirement
if [ "$EUID" -eq 0 ]; then
SUDO_CMD=""
echo "Running as root"
else
if sudo -n true 2>/dev/null; then
SUDO_CMD="sudo"
echo "Running as $CURRENT_USER with sudo privileges"
else
echo "Error: Non-root user requires passwordless sudo access. ❌"
echo "Configure with: echo '$CURRENT_USER ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/$CURRENT_USER"
exit 1
fi
fi
# Check if the OS is manjaro, if so, change it to arch
if [ "$OS_TYPE" = "manjaro" ] || [ "$OS_TYPE" = "manjaro-arm" ]; then
OS_TYPE="arch"
fi
# Check if the OS is Asahi Linux, if so, change it to fedora
if [ "$OS_TYPE" = "fedora-asahi-remix" ]; then
OS_TYPE="fedora"
fi
# Check if the OS is popOS, if so, change it to ubuntu
if [ "$OS_TYPE" = "pop" ]; then
OS_TYPE="ubuntu"
fi
# Check if the OS is linuxmint, if so, change it to ubuntu
if [ "$OS_TYPE" = "linuxmint" ]; then
OS_TYPE="ubuntu"
fi
#Check if the OS is zorin, if so, change it to ubuntu
if [ "$OS_TYPE" = "zorin" ]; then
OS_TYPE="ubuntu"
fi
if [ "$OS_TYPE" = "arch" ] || [ "$OS_TYPE" = "archarm" ]; then
OS_VERSION="rolling"
else
OS_VERSION=$(grep -w "VERSION_ID" /etc/os-release | cut -d "=" -f 2 | tr -d '"')
fi
# Ubuntu 26.04 (resolute) ships a docker-ce repo that has no 28.5.0, so the
# default pin is absent from apt-cache madison and get.docker.com aborts before
# installing Docker. Repin to a version present on every architecture Docker
# ships resolute for: amd64/arm64/armhf start at 29.3.1, but s390x only carries
# 29.4.0-29.4.2, so 29.4.2 is the newest version common to all of them. This
# must run after OS_VERSION is resolved and before the banner so the reported
# Docker version stays accurate.
if [ "$OS_TYPE" = "ubuntu" ] && [ "$OS_VERSION" = "26.04" ]; then
DOCKER_VERSION=29.4.2
fi
if [ "$OS_TYPE" = 'amzn' ]; then
$SUDO_CMD dnf install -y findutils >/dev/null
fi
case "$OS_TYPE" in
arch | ubuntu | debian | raspbian | centos | fedora | rhel | ol | rocky | sles | opensuse-leap | opensuse-tumbleweed | almalinux | opencloudos | amzn | alpine) ;;
*)
echo "This script only supports Debian, Redhat, Arch Linux, Alpine Linux, or SLES based operating systems for now."
exit
;;
esac
${reportDockerVersion()}
echo -e "---------------------------------------------"
echo "| CPU Architecture | $SYS_ARCH"
echo "| Operating System | $OS_TYPE $OS_VERSION"
echo "| Docker | $DOCKER_VERSION_REPORT"
${isBuildServer ? 'echo "| Server Type | Build Server"' : ""}
echo -e "---------------------------------------------\n"
echo -e "1. Installing required packages (curl, wget, git, jq, openssl). "
command_exists() {
command -v "$@" > /dev/null 2>&1
}
${installUtilities()}
${
!isBuildServer
? `
echo -e "2. Validating ports. "
${validatePorts()}
echo -e "3. Installing RClone. "
${installRClone()}
echo -e "4. Installing Docker. "
${installDocker()}
echo -e "5. Setting up Docker Swarm"
${setupSwarm()}
echo -e "6. Setting up Network"
${setupNetwork()}
echo -e "7. Setting up Directories"
${setupMainDirectory()}
${setupDirectories()}
echo -e "8. Setting up Traefik"
${createTraefikConfig()}
echo -e "9. Setting up Middlewares"
${createDefaultMiddlewares()}
echo -e "10. Setting up Traefik Instance"
${createTraefikInstance()}
echo -e "11. Installing Nixpacks"
${installNixpacks()}
echo -e "12. Installing Buildpacks"
${installBuildpacks()}
echo -e "13. Installing Railpack"
${installRailpack()}
echo -e "14. Configuring permissions"
${setupPermissions()}
`
: `
echo -e "2. Installing Docker. "
${installDocker()}
echo -e "3. Setting up Directories"
${setupMainDirectory()}
${setupDirectories()}
echo -e "4. Installing Nixpacks"
${installNixpacks()}
echo -e "5. Installing Buildpacks"
${installBuildpacks()}
echo -e "6. Installing Railpack"
${installRailpack()}
echo -e "7. Configuring permissions"
${setupPermissions()}
`
}
`;
return bashCommand;
};
const installRequirements = async (
serverId: string,
onData?: (data: any) => void,
) => {
const client = new Client();
const server = await findServerById(serverId);
if (!server.sshKeyId) {
onData?.("❌ No SSH Key found, please assign one to this server");
throw new Error("No SSH Key found");
}
const isBuildServer = server.serverType === "build";
return new Promise<void>((resolve, reject) => {
client
.once("ready", () => {
const command = server.command || defaultCommand(isBuildServer);
client.exec(command, (err, stream) => {
if (err) {
onData?.(err.message);
reject(err);
return;
}
stream
.on("close", () => {
client.end();
resolve();
})
.on("data", (data: string) => {
onData?.(data.toString());
})
.stderr.on("data", (data) => {
onData?.(data.toString());
});
});
})
.on("error", (err) => {
client.end();
if (err.level === "client-authentication") {
const technicalDetail = `Error: ${err.message} ${err.level}`;
const friendlyMessage = [
"",
"❌ Couldn't connect to your server — the SSH key was not accepted.",
"",
"This usually means the key doesn't match what's on the server, or the key format is invalid.",
"",
`Technical details: ${technicalDetail}`,
"",
"💡 Hints:",
" • Check that the SSH key you added in Dokploy is the same one installed on the server (e.g. in ~/.ssh/authorized_keys).",
" • Try generating a new SSH key in Dokploy and add only the public key to the server, then try again.",
" • Make sure to follow the instructions on the Setup Server Button on the SSH Keys tab",
].join("\n");
onData?.(friendlyMessage);
reject(
new Error(
`Authentication failed: Invalid SSH private key. ${technicalDetail}`,
),
);
} else {
const technicalDetail = `${err.message} ${err.level ?? ""}`.trim();
const friendlyMessage = [
"",
"❌ Couldn't connect to your server.",
"",
"The connection failed before setup could run. Common causes: wrong IP or port, firewall blocking access, or the server is offline.",
"",
`Technical details: ${technicalDetail}`,
"",
"💡 Hints:",
" • Check that the server IP address and SSH port are correct and the server is powered on.",
" • If the server is in a private network, ensure Dokploy can reach it (VPN, firewall rules, or correct security groups).",
" • Make sure the SSH port (usually 22) is open and the SSH service is running on the server.",
].join("\n");
onData?.(friendlyMessage);
reject(new Error(`SSH connection error: ${technicalDetail}`));
}
})
.connect({
host: server.ipAddress,
port: server.port,
username: server.username,
privateKey: server.sshKey?.privateKey,
});
});
};
const setupDirectories = () => {
const { SSH_PATH } = paths(true);
const directories = Object.values(paths(true));
const createDirsCommand = directories
.map((dir) => `mkdir -p "${dir}"`)
.join(" && ");
const chmodCommand = `chmod 700 "${SSH_PATH}"`;
const command = `
${createDirsCommand}
${chmodCommand}
`;
return command;
};
const setupMainDirectory = () => `
# Check if the /etc/dokploy directory exists
if [ -d /etc/dokploy ]; then
echo "/etc/dokploy already exists ✅"
else
# Create the /etc/dokploy directory
$SUDO_CMD mkdir -p /etc/dokploy
echo "Directory /etc/dokploy created ✅"
fi
# Ensure the current user owns the directory
if [ -n "$SUDO_CMD" ]; then
$SUDO_CMD chown -R $CURRENT_USER:$CURRENT_USER /etc/dokploy
fi
`;
export const setupSwarm = () => `
# Check if the node is already part of a Docker Swarm
if $SUDO_CMD docker info | grep -q 'Swarm: active'; then
echo "Already part of a Docker Swarm ✅"
else
# Get IP address
get_ip() {
local ip=""
# Try IPv4 with multiple services
# First attempt: ifconfig.io
ip=\$(curl -4s --connect-timeout 5 https://ifconfig.io 2>/dev/null)
# Second attempt: icanhazip.com
if [ -z "\$ip" ]; then
ip=\$(curl -4s --connect-timeout 5 https://icanhazip.com 2>/dev/null)
fi
# Third attempt: ipecho.net
if [ -z "\$ip" ]; then
ip=\$(curl -4s --connect-timeout 5 https://ipecho.net/plain 2>/dev/null)
fi
# If no IPv4, try IPv6 with multiple services
if [ -z "\$ip" ]; then
# Try IPv6 with ifconfig.io
ip=\$(curl -6s --connect-timeout 5 https://ifconfig.io 2>/dev/null)
# Try IPv6 with icanhazip.com
if [ -z "\$ip" ]; then
ip=\$(curl -6s --connect-timeout 5 https://icanhazip.com 2>/dev/null)
fi
# Try IPv6 with ipecho.net
if [ -z "\$ip" ]; then
ip=\$(curl -6s --connect-timeout 5 https://ipecho.net/plain 2>/dev/null)
fi
fi
if [ -z "\$ip" ]; then
echo "Error: Could not determine server IP address automatically (neither IPv4 nor IPv6)." >&2
echo "Please set the ADVERTISE_ADDR environment variable manually." >&2
echo "Example: export ADVERTISE_ADDR=<your-server-ip>" >&2
exit 1
fi
echo "\$ip"
}
advertise_addr=\$(get_ip)
echo "Advertise address: \$advertise_addr"
# Initialize Docker Swarm
$SUDO_CMD docker swarm init --advertise-addr \$advertise_addr
echo "Swarm initialized ✅"
fi
`;
const setupNetwork = () => `
# Check if the dokploy-network already exists
if $SUDO_CMD docker network ls | grep -q 'dokploy-network'; then
echo "Network dokploy-network already exists ✅"
else
# Create the dokploy-network if it doesn't exist
if $SUDO_CMD docker network create --driver overlay --attachable dokploy-network; then
echo "Network created ✅"
else
echo "Failed to create dokploy-network ❌" >&2
exit 1
fi
fi
`;
const validatePorts = () => `
# check if something is running on port 80
if ss -tulnp | grep ':80 ' >/dev/null; then
echo "Something is already running on port 80" >&2
fi
# check if something is running on port 443
if ss -tulnp | grep ':443 ' >/dev/null; then
echo "Something is already running on port 443" >&2
fi
`;
const installUtilities = () => `
case "$OS_TYPE" in
arch)
$SUDO_CMD pacman -Sy --noconfirm --needed unzip curl wget git git-lfs jq openssl >/dev/null || true
;;
alpine)
$SUDO_CMD sed -i '/^#.*\/community/s/^#//' /etc/apk/repositories
$SUDO_CMD apk update >/dev/null
$SUDO_CMD apk add curl wget git git-lfs jq openssl sudo unzip tar >/dev/null
;;
ubuntu | debian | raspbian)
export DEBIAN_FRONTEND=noninteractive
$SUDO_CMD apt-get update -y >/dev/null
$SUDO_CMD apt-get install -y unzip curl wget git git-lfs jq openssl >/dev/null
;;
centos | fedora | rhel | ol | rocky | almalinux | opencloudos | amzn)
if [ "$OS_TYPE" = "amzn" ]; then
$SUDO_CMD dnf install -y wget git git-lfs jq openssl >/dev/null
else
if ! command -v dnf >/dev/null; then
$SUDO_CMD yum install -y dnf >/dev/null
fi
if ! command -v curl >/dev/null; then
$SUDO_CMD dnf install -y curl >/dev/null
fi
$SUDO_CMD dnf install -y wget git git-lfs jq openssl unzip >/dev/null
fi
;;
sles | opensuse-leap | opensuse-tumbleweed)
$SUDO_CMD zypper refresh >/dev/null
$SUDO_CMD zypper install -y curl wget git git-lfs jq openssl >/dev/null
;;
*)
echo "This script only supports Debian, Redhat, Arch Linux, or SLES based operating systems for now."
exit
;;
esac
`;
const installDocker = () => `
# Detect if docker is installed via snap
if [ -x "$(command -v snap)" ]; then
SNAP_DOCKER_INSTALLED=$(snap list docker >/dev/null 2>&1 && echo "true" || echo "false")
if [ "$SNAP_DOCKER_INSTALLED" = "true" ]; then
echo " - Docker is installed via snap."
echo " Please note that Dokploy does not support Docker installed via snap."
echo " Please remove Docker with snap (snap remove docker) and reexecute this script."
exit 1
fi
fi
echo -e "3. Check Docker Installation. "
if ! [ -x "$(command -v docker)" ]; then
echo " - Docker is not installed. Installing Docker. It may take a while."
case "$OS_TYPE" in
"almalinux" | "rocky" | "centos" | "rhel" | "ol")
$SUDO_CMD dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo >/dev/null 2>&1
$SUDO_CMD dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin >/dev/null 2>&1
if ! [ -x "$(command -v docker)" ]; then
echo " - Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue."
exit 1
fi
$SUDO_CMD systemctl start docker >/dev/null 2>&1
$SUDO_CMD systemctl enable docker >/dev/null 2>&1
;;
"opencloudos")
# Special handling for OpenCloud OS
echo " - Installing Docker for OpenCloud OS..."
$SUDO_CMD dnf install -y docker >/dev/null 2>&1
if ! [ -x "$(command -v docker)" ]; then
echo " - Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue."
exit 1
fi
# Remove --live-restore parameter from Docker configuration if it exists
if [ -f "/etc/sysconfig/docker" ]; then
echo " - Removing --live-restore parameter from Docker configuration..."
$SUDO_CMD sed -i 's/--live-restore[^[:space:]]*//' /etc/sysconfig/docker >/dev/null 2>&1
$SUDO_CMD sed -i 's/--live-restore//' /etc/sysconfig/docker >/dev/null 2>&1
# Clean up any double spaces that might be left
$SUDO_CMD sed -i 's/ */ /g' /etc/sysconfig/docker >/dev/null 2>&1
fi
$SUDO_CMD systemctl enable docker >/dev/null 2>&1
$SUDO_CMD systemctl start docker >/dev/null 2>&1
echo " - Docker configured for OpenCloud OS"
;;
"alpine")
$SUDO_CMD apk add docker docker-cli-compose >/dev/null 2>&1
$SUDO_CMD rc-update add docker default >/dev/null 2>&1
$SUDO_CMD service docker start >/dev/null 2>&1
if ! [ -x "$(command -v docker)" ]; then
echo " - Failed to install Docker with apk. Try to install it manually."
echo " Please visit https://wiki.alpinelinux.org/wiki/Docker for more information."
exit 1
fi
;;
"arch")
$SUDO_CMD pacman -Sy docker docker-compose --noconfirm >/dev/null 2>&1
$SUDO_CMD systemctl enable docker.service >/dev/null 2>&1
if ! [ -x "$(command -v docker)" ]; then
echo " - Failed to install Docker with pacman. Try to install it manually."
echo " Please visit https://wiki.archlinux.org/title/docker for more information."
exit 1
fi
;;
"amzn")
$SUDO_CMD dnf install docker -y >/dev/null 2>&1
DOCKER_CONFIG=/usr/local/lib/docker
$SUDO_CMD mkdir -p $DOCKER_CONFIG/cli-plugins >/dev/null 2>&1
$SUDO_CMD curl -sL https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o $DOCKER_CONFIG/cli-plugins/docker-compose >/dev/null 2>&1
$SUDO_CMD chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose >/dev/null 2>&1
$SUDO_CMD systemctl start docker >/dev/null 2>&1
$SUDO_CMD systemctl enable docker >/dev/null 2>&1
if ! [ -x "$(command -v docker)" ]; then
echo " - Failed to install Docker with dnf. Try to install it manually."
echo " Please visit https://www.cyberciti.biz/faq/how-to-install-docker-on-amazon-linux-2/ for more information."
exit 1
fi
;;
"fedora")
if [ -x "$(command -v dnf5)" ]; then
# dnf5 is available
$SUDO_CMD dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/fedora/docker-ce.repo --overwrite >/dev/null 2>&1
else
# dnf5 is not available, use dnf
$SUDO_CMD dnf config-manager --add-repo=https://download.docker.com/linux/fedora/docker-ce.repo >/dev/null 2>&1
fi
$SUDO_CMD dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin >/dev/null 2>&1
if ! [ -x "$(command -v docker)" ]; then
echo " - Docker could not be installed automatically. Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue."
exit 1
fi
$SUDO_CMD systemctl start docker >/dev/null 2>&1
$SUDO_CMD systemctl enable docker >/dev/null 2>&1
;;
*)
if [ "$OS_TYPE" = "ubuntu" ] && [ "$OS_VERSION" = "24.10" ]; then
echo "Docker automated installation is not supported on Ubuntu 24.10 (non-LTS release)."
echo "Please install Docker manually."
exit 1
fi
if ! [ -x "$(command -v docker)" ]; then
curl -s https://get.docker.com | $SUDO_CMD sh -s -- --version $DOCKER_VERSION 2>&1
if ! [ -x "$(command -v docker)" ]; then
echo " - Docker installation failed."
echo " Maybe your OS is not supported?"
echo " - Please visit https://docs.docker.com/engine/install/ and install Docker manually to continue."
exit 1
fi
fi
if [ "$OS_TYPE" = "rocky" ]; then
$SUDO_CMD systemctl start docker >/dev/null 2>&1
$SUDO_CMD systemctl enable docker >/dev/null 2>&1
fi
if [ "$OS_TYPE" = "centos" ]; then
$SUDO_CMD systemctl start docker >/dev/null 2>&1
$SUDO_CMD systemctl enable docker >/dev/null 2>&1
fi
esac
echo " - Docker installed successfully."
else
echo " - Docker is installed."
fi
`;
const createTraefikConfig = () => {
const config = getDefaultServerTraefikConfig();
const command = `
if [ -f "/etc/dokploy/traefik/dynamic/acme.json" ]; then
chmod 600 "/etc/dokploy/traefik/dynamic/acme.json"
fi
if [ -f "/etc/dokploy/traefik/traefik.yml" ]; then
echo "Traefik config already exists ✅"
else
echo "${config}" > /etc/dokploy/traefik/traefik.yml
fi
`;
return command;
};
const createDefaultMiddlewares = () => {
const config = getDefaultMiddlewares();
const command = `
if [ -f "/etc/dokploy/traefik/dynamic/middlewares.yml" ]; then
echo "Middlewares config already exists ✅"
else
echo "${config}" > /etc/dokploy/traefik/dynamic/middlewares.yml
fi
`;
return command;
};
export const installRClone = () => `
if command_exists rclone; then
echo "RClone already installed ✅"
else
curl https://rclone.org/install.sh | $SUDO_CMD bash
RCLONE_VERSION=$(rclone --version | head -n 1 | awk '{print $2}' | sed 's/^v//')
echo "RClone version $RCLONE_VERSION installed ✅"
fi
`;
export const createTraefikInstance = () => {
const command = `
# Check if dokpyloy-traefik exists
if $SUDO_CMD docker service inspect dokploy-traefik > /dev/null 2>&1; then
echo "Migrating Traefik to Standalone..."
$SUDO_CMD docker service rm dokploy-traefik
sleep 8
echo "Traefik migrated to Standalone ✅"
fi
if $SUDO_CMD docker inspect dokploy-traefik > /dev/null 2>&1; then
echo "Traefik already exists ✅"
else
# Create the dokploy-traefik container
TRAEFIK_VERSION=${TRAEFIK_VERSION}
$SUDO_CMD docker run -d \
--name dokploy-traefik \
--restart always \
-v /etc/dokploy/traefik/traefik.yml:/etc/traefik/traefik.yml \
-v /etc/dokploy/traefik/dynamic:/etc/dokploy/traefik/dynamic \
-v /var/run/docker.sock:/var/run/docker.sock \
-p ${TRAEFIK_SSL_PORT}:${TRAEFIK_SSL_PORT} \
-p ${TRAEFIK_PORT}:${TRAEFIK_PORT} \
-p ${TRAEFIK_HTTP3_PORT}:${TRAEFIK_HTTP3_PORT}/udp \
traefik:v$TRAEFIK_VERSION
$SUDO_CMD docker network connect dokploy-network dokploy-traefik;
echo "Traefik version $TRAEFIK_VERSION installed ✅"
fi
`;
return command;
};
const installNixpacks = () => `
if command_exists nixpacks; then
echo "Nixpacks already installed ✅"
else
export NIXPACKS_VERSION=1.41.0
$SUDO_CMD bash -c "$(curl -fsSL https://nixpacks.com/install.sh)"
echo "Nixpacks version $NIXPACKS_VERSION installed ✅"
fi
`;
const installRailpack = () => `
if command_exists railpack; then
echo "Railpack already installed ✅"
else
export RAILPACK_VERSION=0.15.4
$SUDO_CMD bash -c "$(curl -fsSL https://railpack.com/install.sh)"
echo "Railpack version $RAILPACK_VERSION installed ✅"
fi
`;
const setupPermissions = () => `
# Add user to docker group if not root
if [ -n "$SUDO_CMD" ]; then
if ! groups $CURRENT_USER | grep -qw docker; then
$SUDO_CMD usermod -aG docker $CURRENT_USER
echo "User $CURRENT_USER added to docker group ✅"
else
echo "User $CURRENT_USER already in docker group ✅"
fi
# Ensure the user owns the dokploy directory
$SUDO_CMD chown -R $CURRENT_USER:$CURRENT_USER /etc/dokploy
echo "Permissions configured for $CURRENT_USER ✅"
else
echo "Running as root, no extra permissions needed ✅"
fi
`;
const installBuildpacks = () => `
SUFFIX=""
if [ "$SYS_ARCH" = "aarch64" ] || [ "$SYS_ARCH" = "arm64" ]; then
SUFFIX="-arm64"
fi
if command_exists pack; then
echo "Buildpacks already installed ✅"
else
BUILDPACKS_VERSION=0.39.1
curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.39.1/pack-v$BUILDPACKS_VERSION-linux$SUFFIX.tgz" | $SUDO_CMD tar -C /usr/local/bin/ --no-same-owner -xzv pack
echo "Buildpacks version $BUILDPACKS_VERSION installed ✅"
fi
`;