diff --git a/.github/workflows/installer.yml b/.github/workflows/installer.yml new file mode 100644 index 00000000..405bc7b9 --- /dev/null +++ b/.github/workflows/installer.yml @@ -0,0 +1,196 @@ +name: Installer quality + +on: + pull_request: + paths: + - apps/website/public/install.sh + - apps/website/public/install.sh.sha256 + - apps/docs/content/docs/core/installation.mdx + - apps/docs/content/docs/core/manual-installation.mdx + - apps/docs/content/docs/core/troubleshooting/networking.mdx + - apps/website/components/Hero.tsx + - apps/website/app/self-hosted-paas/page.tsx + - tests/installer/** + - .github/workflows/installer.yml + push: + branches: [main, canary] + paths: + - apps/website/public/install.sh + - apps/website/public/install.sh.sha256 + - apps/docs/content/docs/core/installation.mdx + - apps/docs/content/docs/core/manual-installation.mdx + - apps/docs/content/docs/core/troubleshooting/networking.mdx + - apps/website/components/Hero.tsx + - apps/website/app/self-hosted-paas/page.tsx + - tests/installer/** + - .github/workflows/installer.yml + workflow_dispatch: + schedule: + - cron: "17 3 * * 1" + +permissions: + contents: read + +jobs: + shell-quality: + name: ShellCheck, shfmt, syntax, and Bats + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Install shell quality tools + run: sudo apt-get update && sudo apt-get install -y bats shellcheck shfmt + - name: Validate Bash syntax + run: bash -n apps/website/public/install.sh + - name: Verify published installer checksum + working-directory: apps/website/public + run: sha256sum --check install.sh.sha256 + - name: Run ShellCheck + run: shellcheck --severity=style apps/website/public/install.sh tests/installer/install.bats + - name: Verify shfmt + run: shfmt -d -i 0 -ci apps/website/public/install.sh tests/installer/install.bats + - name: Run installer unit tests + run: bats tests/installer/install.bats + - name: Verify checksum tamper detection + run: | + checksum_dir=$(mktemp -d) + cp apps/website/public/install.sh "$checksum_dir/install.sh" + cd "$checksum_dir" + sha256sum install.sh >install.sh.sha256 + sha256sum --check install.sh.sha256 + printf '\n# tampered\n' >>install.sh + if sha256sum --check install.sh.sha256; then + echo "Checksum verification accepted a modified installer" >&2 + exit 1 + fi + + platform-contract: + name: Platform contract (${{ matrix.name }}) + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + include: + - name: Ubuntu 22.04 amd64 + image: ubuntu:22.04 + - name: Ubuntu 24.04 amd64 + image: ubuntu:24.04 + - name: Debian 12 amd64 + image: debian:12 + steps: + - uses: actions/checkout@v4 + - name: Validate platform and architecture detection + run: | + docker run --rm \ + -v "$PWD:/work:ro" \ + -w /work \ + "${{ matrix.image }}" \ + bash -c 'source ./apps/website/public/install.sh; detect_platform; test "$PLATFORM_CLASS" = supported; test "$ARCHITECTURE" = amd64' + + arm64-contract: + name: Platform contract (Ubuntu 24.04 arm64) + runs-on: ubuntu-24.04-arm + steps: + - uses: actions/checkout@v4 + - name: Validate arm64 architecture detection + run: | + docker run --rm \ + -v "$PWD:/work:ro" \ + -w /work \ + ubuntu:24.04 \ + bash -c 'source ./apps/website/public/install.sh; detect_platform; test "$PLATFORM_CLASS" = supported; test "$ARCHITECTURE" = arm64' + + integration-versions: + name: Resolve integration upgrade path + if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' + runs-on: ubuntu-24.04 + outputs: + initial: ${{ steps.versions.outputs.initial }} + target: ${{ steps.versions.outputs.target }} + steps: + - name: Resolve stable upgrade pair + id: versions + env: + GH_TOKEN: ${{ github.token }} + run: | + if [ "$GITHUB_REF_NAME" = "main" ]; then + releases=$(curl --fail --show-error --location --retry 5 \ + -H "Authorization: Bearer $GH_TOKEN" \ + 'https://api.github.com/repos/Dokploy/dokploy/releases?per_page=10') + initial=$(node -e ' + const releases = JSON.parse(process.argv[1]).filter((release) => !release.draft && !release.prerelease); + if (releases.length < 2) process.exit(1); + process.stdout.write(releases[1].tag_name); + ' "$releases") + echo "initial=$initial" >>"$GITHUB_OUTPUT" + echo "target=latest" >>"$GITHUB_OUTPUT" + else + echo "initial=latest" >>"$GITHUB_OUTPUT" + echo "target=canary" >>"$GITHUB_OUTPUT" + fi + + vm-integration: + name: VM integration (${{ matrix.runner }}) + needs: integration-versions + runs-on: ${{ matrix.runner }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + runner: [ubuntu-22.04, ubuntu-24.04, ubuntu-24.04-arm] + steps: + - uses: actions/checkout@v4 + - name: Check host readiness without changes + run: sudo env DOKPLOY_INSTALL_ACTION=check DOKPLOY_VERSION="${{ needs.integration-versions.outputs.initial }}" DOKPLOY_ALLOW_LOW_RESOURCES=1 bash apps/website/public/install.sh + - name: Fresh installation on a systemd VM + run: sudo env DOKPLOY_VERSION="${{ needs.integration-versions.outputs.initial }}" DOKPLOY_ALLOW_LOW_RESOURCES=1 DOKPLOY_STABILIZATION_SECONDS=3 bash apps/website/public/install.sh + - name: Idempotent rerun + run: sudo env DOKPLOY_VERSION="${{ needs.integration-versions.outputs.initial }}" DOKPLOY_ALLOW_LOW_RESOURCES=1 DOKPLOY_STABILIZATION_SECONDS=3 bash apps/website/public/install.sh + - name: Upgrade and validate + run: sudo env DOKPLOY_VERSION="${{ needs.integration-versions.outputs.target }}" DOKPLOY_ALLOW_LOW_RESOURCES=1 DOKPLOY_STABILIZATION_SECONDS=3 bash apps/website/public/install.sh + - name: Preserve failure diagnostics + if: failure() + uses: actions/upload-artifact@v4 + with: + name: installer-diagnostics-${{ matrix.runner }} + path: /var/log/dokploy/ + if-no-files-found: ignore + + debian-vm-integration: + name: VM integration (Debian 12 amd64) + needs: integration-versions + runs-on: ubuntu-24.04 + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - uses: lima-vm/lima-actions/setup@v1 + id: lima + - uses: actions/cache@v4 + with: + path: ~/.cache/lima + key: lima-${{ steps.lima.outputs.version }}-debian-12 + - name: Start Debian 12 systemd VM + run: limactl start --plain --name=debian --cpus=4 --memory=6 --disk=40 template://debian-12 + - name: Copy installer sources into VM + run: | + ssh lima-debian mkdir -p /tmp/dokploy + scp apps/website/public/install.sh lima-debian:/tmp/dokploy/install.sh + - name: Check host readiness without changes + run: ssh lima-debian sudo env DOKPLOY_INSTALL_ACTION=check DOKPLOY_VERSION="${{ needs.integration-versions.outputs.initial }}" bash /tmp/dokploy/install.sh + - name: Fresh install, idempotent rerun, and upgrade + run: | + ssh lima-debian sudo env DOKPLOY_VERSION="${{ needs.integration-versions.outputs.initial }}" DOKPLOY_STABILIZATION_SECONDS=3 bash /tmp/dokploy/install.sh + ssh lima-debian sudo env DOKPLOY_VERSION="${{ needs.integration-versions.outputs.initial }}" DOKPLOY_STABILIZATION_SECONDS=3 bash /tmp/dokploy/install.sh + ssh lima-debian sudo env DOKPLOY_VERSION="${{ needs.integration-versions.outputs.target }}" DOKPLOY_STABILIZATION_SECONDS=3 bash /tmp/dokploy/install.sh + - name: Collect failure diagnostics + if: failure() + run: | + if ssh lima-debian 'sudo tar -C /var/log -czf /tmp/dokploy-diagnostics.tar.gz dokploy'; then + scp lima-debian:/tmp/dokploy-diagnostics.tar.gz . + fi + - name: Upload failure diagnostics + if: failure() + uses: actions/upload-artifact@v4 + with: + name: installer-diagnostics-debian-12 + path: dokploy-diagnostics.tar.gz + if-no-files-found: ignore diff --git a/apps/docs/content/docs/core/installation.mdx b/apps/docs/content/docs/core/installation.mdx index 9b95509d..081fada5 100644 --- a/apps/docs/content/docs/core/installation.mdx +++ b/apps/docs/content/docs/core/installation.mdx @@ -13,19 +13,17 @@ You need to follow this steps in the same order: There are multiple VPS providers to choose from: -We have tested on the following Linux Distros: - -- Ubuntu 24.04 LTS -- Ubuntu 23.10 -- Ubuntu 22.04 LTS -- Ubuntu 20.04 LTS -- Ubuntu 18.04 LTS -- Debian 12 -- Debian 11 -- Debian 10 -- Fedora 40 -- Centos 9 -- Centos 8 +The production installer supports these 64-bit platforms: + +- Ubuntu 24.04 LTS (`amd64` and `arm64`) +- Ubuntu 22.04 LTS (`amd64` and `arm64`) +- Debian 12 (`amd64` and `arm64`) + +Other systemd-based Linux distributions with an existing supported Docker +Engine installation are experimental and require +`DOKPLOY_ALLOW_UNSUPPORTED=1`. Rootless Docker, Docker installed through Snap, +Podman aliases, Docker Desktop, remote Docker contexts, Docker/OpenVZ containers, +and WSL are not supported server environments. ### Providers @@ -42,7 +40,10 @@ We have tested on the following Linux Distros: ### Requirements -To ensure a smooth experience with Dokploy, your server should have at least 2GB of RAM and 30GB of disk space. This specification helps to handle the resources consumed by Docker during builds and prevents system freezes. +Dokploy requires at least 2 GB of RAM and 10 GB of free disk space. Four GB of +RAM and 30 GB of free disk space are recommended for Docker builds and backups. +Systems with 1–2 GB of RAM require `DOKPLOY_ALLOW_LOW_RESOURCES=1`; systems below +1 GB are rejected. Linux kernel 5.4+, systemd, and Bash 4.3+ are required. import { Callout } from "fumadocs-ui/components/callout"; @@ -73,48 +74,104 @@ Dokploy utilizes Docker, so it is essential to have Docker installed on your ser Dokploy Cloud: Use Dokploy directly without worrying about maintenance or updates. Enjoy a hassle-free experience with Dokploy Cloud. Sign up +For an inspectable installation, download the installer and its checksum first: + +```bash +curl --fail --show-error --location --retry 5 \ + --output install.sh https://dokploy.com/install.sh +curl --fail --show-error --location --retry 5 \ + --output install.sh.sha256 https://dokploy.com/install.sh.sha256 +sha256sum --check install.sh.sha256 +less install.sh +sudo bash install.sh +``` + +The shorter convenience command is also available: + ```bash -curl -sSL https://dokploy.com/install.sh | sh +curl --fail --show-error --location https://dokploy.com/install.sh | sudo bash ``` +The installer requires Bash; do not invoke it with `sh`. + See [Manual Installation](/docs/core/manual-installation) if you want to customize your Dokploy installation. +### Check Your Server Before Installation + +Check mode performs platform, dependency, resource, filesystem, port, Docker, +DNS, TLS, registry, version, and configuration validation without installing +packages, writing files, pulling images, creating Docker resources, or restarting +services: + +```bash +curl --fail --show-error --location --output install.sh https://dokploy.com/install.sh +sudo DOKPLOY_INSTALL_ACTION=check bash install.sh +``` + +`DRY_RUN=1` is an alias for check mode and also prints the actions that would be +performed. + ### Advanced Installation Options -The installation script automatically detects and installs the latest stable version from GitHub. However, you can customize the installation using environment variables: +The installer resolves `latest` to an immutable stable release tag before +changing the host. All important choices can be supplied noninteractively with +environment variables. #### Install Specific Versions **Install Canary Version (Development):** ```bash -export DOKPLOY_VERSION=canary && curl -sSL https://dokploy.com/install.sh | sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_VERSION=canary bash ``` **Install Latest Stable:** ```bash -export DOKPLOY_VERSION=latest && curl -sSL https://dokploy.com/install.sh | sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_VERSION=latest bash ``` **Install a Specific Version:** -Every [Dokploy release](https://github.com/Dokploy/dokploy/releases) includes its own `install.sh` as a release asset, which installs exactly that version with the setup it expects. Replace the version tag in the URL with the one you want: +Pin the requested version for reproducible provisioning: ```bash -curl -sL https://github.com/Dokploy/dokploy/releases/download/v0.26.6/install.sh | sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_VERSION=v0.29.13 bash ``` - Don't use `DOKPLOY_VERSION` to install a specific version — the main script at `dokploy.com/install.sh` always targets the latest release, so its setup may not be compatible with older versions. + Review release notes and test upgrade paths before pinning an older or breaking + version. Major-version transitions and downgrades require + `DOKPLOY_ALLOW_BREAKING_UPGRADE=1`. +#### Installation Actions + +`DOKPLOY_INSTALL_ACTION` accepts the following values: + +- `auto` (default): install, upgrade, repair, or validate according to detected state +- `install`: require a completely fresh Dokploy state +- `upgrade`: require an existing healthy installation with a different target version +- `repair`: recreate only missing or unhealthy resources while preserving data +- `abort`: report the detected state and stop +- `check`: run the non-destructive preflight only + +The installer classifies existing hosts as fresh, healthy, upgradeable, in need +of repair, partial, or conflicting. A healthy rerun performs validation without +rotating secrets or recreating resources. + #### Custom Network Configuration -If you need to customize the Docker Swarm network configuration (useful to avoid CIDR conflicts with cloud provider VPCs): +If you need to customize Docker Swarm's address pool to avoid a VPC CIDR +conflict, pass the legacy `DOCKER_SWARM_INIT_ARGS` option inline. The installer +splits this value without `eval`, so quoted values containing spaces are not +supported: ```bash -export DOCKER_SWARM_INIT_ARGS="--default-addr-pool 172.20.0.0/16 --default-addr-pool-mask-length 24" -curl -sSL https://dokploy.com/install.sh | sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOCKER_SWARM_INIT_ARGS="--default-addr-pool 172.20.0.0/16 --default-addr-pool-mask-length 24" bash ``` #### Manual Advertise Address @@ -122,11 +179,14 @@ curl -sSL https://dokploy.com/install.sh | sh If the script cannot detect your server's IP automatically, or you want the Swarm to use a specific interface (e.g. a VPN/WireGuard IP), specify it manually: ```bash -curl -sSL https://dokploy.com/install.sh | sudo ADVERTISE_ADDR=192.168.1.100 sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_ADVERTISE_ADDR=192.168.1.100 bash ``` - If you run the installer with `sudo`, don't rely on `export ADVERTISE_ADDR=...` from your user shell — `sudo` resets the environment, so the variable never reaches the script and it falls back to auto-detection. Pass the variable inline as shown above, or use `sudo -E sh` to preserve your environment. The same applies to `DOKPLOY_VERSION`, `DOCKER_SWARM_INIT_ARGS`, and `ENDPOINT_MODE`. + Do not rely on variables exported before `sudo`, because `sudo` normally + resets the environment. Pass variables with `sudo env` as shown above. + `ADVERTISE_ADDR` and `ENDPOINT_MODE` remain supported as deprecated aliases. #### Kernels Without IPVS Support @@ -134,7 +194,8 @@ curl -sSL https://dokploy.com/install.sh | sudo ADVERTISE_ADDR=192.168.1.100 sh Docker Swarm's default service discovery requires IPVS support in the kernel. Some minimal or appliance-style distributions (e.g. ZimaOS and other Buildroot-based images) ship kernels without it. On those systems, install with DNSRR endpoint mode: ```bash -curl -sSL https://dokploy.com/install.sh | sudo ENDPOINT_MODE=dnsrr sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_ENDPOINT_MODE=dnsrr DOKPLOY_ALLOW_UNSUPPORTED=1 bash ``` See [Services Can't Reach Each Other (Missing IPVS Kernel Modules)](/docs/core/troubleshooting/networking#services-cant-reach-each-other-missing-ipvs-kernel-modules) for how to detect this situation and its trade-offs. @@ -142,7 +203,9 @@ See [Services Can't Reach Each Other (Missing IPVS Kernel Modules)](/docs/core/t ### Proxmox LXC Support - The installation script automatically detects Proxmox LXC containers and applies the necessary configurations (`--endpoint-mode dnsrr`) for compatibility. + Proxmox LXC is experimental. The installer detects it, requires + `DOKPLOY_ALLOW_UNSUPPORTED=1`, and automatically uses DNSRR endpoint mode. + Docker and OpenVZ containers remain unsupported. ### Updating Dokploy @@ -150,17 +213,90 @@ See [Services Can't Reach Each Other (Missing IPVS Kernel Modules)](/docs/core/t To update your Dokploy installation to the latest version: ```bash -curl -sSL https://dokploy.com/install.sh | sh -s update +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_INSTALL_ACTION=upgrade bash ``` **Update to Specific Version:** -Use the `install.sh` from that version's [GitHub release](https://github.com/Dokploy/dokploy/releases): +Pin the target version explicitly: ```bash -curl -sL https://github.com/Dokploy/dokploy/releases/download/v0.26.6/install.sh | sh -s update +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_INSTALL_ACTION=upgrade DOKPLOY_VERSION=v0.29.13 bash ``` +Before an upgrade, the installer records the installed and target versions, +classifies the upgrade, validates compatibility, creates a protected backup, +pulls images, and resolves them to immutable repository digests. It validates +the database, application, mounts, networks, reverse proxy, restart counts, and +a stabilization period before reporting success. + +Automatic rollback is enabled only for an upgrade pair explicitly marked +`database-safe` in `DOKPLOY_ROLLBACK_COMPATIBILITY_FILE`. Otherwise the backup +is preserved for manual recovery instead of risking an unsafe database restore. + +### Ports and Existing Reverse Proxies + +Use `DOKPLOY_HTTP_PORT`, `DOKPLOY_HTTPS_PORT`, and `DOKPLOY_APP_PORT` to choose +alternate host ports. If an existing proxy will route to Dokploy, set +`DOKPLOY_SKIP_TRAEFIK=1`. `DOKPLOY_ALLOW_PORT_CONFLICT=1` acknowledges a known +conflict, but does not make two processes able to bind the same port. + +### Configuration Reference + +All installer input is validated before host changes. Boolean options accept +only `0` or `1`; ports must be from 1 to 65535; paths must be absolute; and +versions must be `latest`, `canary`, or a release tag such as `v0.29.13`. + +| Variable | Default | Purpose and scope | +| --- | --- | --- | +| `DOKPLOY_VERSION` | `latest` | Immutable target release for install or upgrade. | +| `DOKPLOY_INSTALL_ACTION` | `auto` | Selects `auto`, `install`, `upgrade`, `repair`, `abort`, or `check`. | +| `DOKPLOY_DATA_DIR` | `/etc/dokploy` | Protected host configuration directory. Existing content is backed up before replacement. | +| `DOKPLOY_BACKUP_DIR` | `/var/backups/dokploy` | Upgrade backup location; should be on a trusted local filesystem. | +| `DOKPLOY_LOG_DIR` | `/var/log/dokploy` | Persistent installer logs and failure diagnostics. | +| `DOKPLOY_HTTP_PORT` / `DOKPLOY_HTTPS_PORT` / `DOKPLOY_APP_PORT` | `80` / `443` / `3000` | Validated host ports used during installation and health checks. | +| `DOKPLOY_NETWORK_NAME` | `dokploy-network` | Overlay network name; must be a safe Docker resource name. | +| `DOKPLOY_ADVERTISE_ADDR` | auto-detected | IPv4/IPv6 address or interface advertised by a newly initialized Swarm. | +| `DOKPLOY_ENDPOINT_MODE` | `auto` | `auto`, `vip`, or `dnsrr`; DNSRR is selected automatically when IPVS is unavailable. | +| `DOKPLOY_DOMAIN` | empty | Optional domain used for the HTTPS routing health check. | +| `DOKPLOY_EMAIL` | empty | Validated contact metadata reserved for installation automation; it is not logged. | +| `DOKPLOY_TIMEZONE` | `UTC` | Validated IANA timezone for installation metadata. | +| `DOKPLOY_SKIP_DOCKER_INSTALL` | `0` | Require an existing supported Docker Engine instead of installing it. | +| `DOKPLOY_SKIP_TRAEFIK` | `0` | Integrate with an existing reverse proxy rather than create Dokploy's Traefik container. | +| `DOKPLOY_REUSE_EXISTING_SWARM` | `0` | Permit installation into an existing manager Swarm without leaving or reinitializing it. | +| `DOKPLOY_ALLOW_PORT_CONFLICT` | `0` | Explicitly acknowledge detected port ownership; use only after verifying the integration. | +| `DOKPLOY_ALLOW_UNSUPPORTED` | `0` | Opt into an experimental platform after reviewing the preflight report. | +| `DOKPLOY_ALLOW_LOW_RESOURCES` | `0` | Permit 1–2 GB RAM or capacity below the recommended values, but never below hard limits. | +| `DOKPLOY_ALLOW_BREAKING_UPGRADE` | `0` | Explicitly permit a major-version transition or downgrade. | +| `DOKPLOY_MIN_UPGRADE_VERSION` | empty | Optional minimum installed version accepted by an automated upgrade policy. | +| `DOKPLOY_ROLLBACK_COMPATIBILITY_FILE` | data-directory metadata file | Lists `old-version new-version database-safe` upgrade pairs eligible for automatic rollback. | +| `DOKPLOY_DIAGNOSTICS` | `1` | Create a local, sanitized failure bundle; set to `0` to opt out. Nothing is uploaded. | +| `DOKPLOY_HEALTH_ATTEMPTS` / `DOKPLOY_HEALTH_INTERVAL` | `45` / `2` | Number and interval in seconds for health retries. | +| `DOKPLOY_STABILIZATION_SECONDS` | `10` | Required healthy period before installation success. | +| `DOKPLOY_DOCKER_VERSION` | `28.5` | Docker package stream installed on supported distributions when Docker is absent. | +| `DOKPLOY_LOCK_FILE` | `/var/lock/dokploy-installer.lock` | `flock` lock preventing concurrent installer runs. | +| `DOKPLOY_NONINTERACTIVE` | auto | Forces automation-friendly behavior; no prompt is required by default. | +| `DRY_RUN` | `0` | Alias for non-destructive check mode. | +| `DEBUG` | `0` | Enables Bash tracing except around secret operations. | +| `NO_COLOR` | empty | Disables color; non-terminal output is plain automatically. | + +### Logs, Backups, and Diagnostics + +Every run writes a timestamped log under `/var/log/dokploy`, including installer +and target versions, the preflight report, commands, upgrades, warnings, and +health results. On failure, a sanitized diagnostics archive is created in the +same directory by default. It includes host and Docker state but redacts common +password, token, key, cookie, and connection-string patterns; application +environment variables are excluded. Review any bundle before sharing it. + +Upgrade backups are written under `/var/backups/dokploy` with restrictive +permissions. They preserve installer metadata, relevant host configuration, +service specifications, image references, and a PostgreSQL dump. Cleanup never +deletes persistent volumes or existing user data, and a database dump is never +restored automatically unless the upgrade path is explicitly declared safe. + ## Completing the Setup After running the installation script, Dokploy and its dependencies will be set up on your server. Here's how to finalize the setup and start using Dokploy: @@ -207,4 +343,3 @@ docker service update --publish-rm "published=3000,target=3000,mode=host" dokplo ``` To further secure your installation, consider reading the [Security recommendations](/docs/core/remote-servers/security#security-recommendations) section. - diff --git a/apps/docs/content/docs/core/manual-installation.mdx b/apps/docs/content/docs/core/manual-installation.mdx index 1b55ee8d..7e21ecef 100644 --- a/apps/docs/content/docs/core/manual-installation.mdx +++ b/apps/docs/content/docs/core/manual-installation.mdx @@ -3,258 +3,49 @@ title: 'Manual Installation' description: 'Learn how to manually install Dokploy on your server.' --- -If you wish to customize the Dokploy installation on your server, you can modify several environment variables: +The production installer is the canonical, audited source for provisioning Dokploy. +It supports version pinning and noninteractive customization without requiring a +fork of the script. See [Installation](/docs/core/installation) for the complete +configuration reference. -1. **PORT** - Ideal for avoiding conflicts with other services. -2. **TRAEFIK_SSL_PORT** - Set to another port if you want to use a different port for SSL. -3. **TRAEFIK_PORT** - Set to another port if you want to use a different port for Traefik. -4. **ADVERTISE_ADDR** - Set to another IP address if you want to use a different IP address for Swarm. -5. **RELEASE_TAG** - Set to a dokploy docker hub tag(latest, canary, feature, etc) -6. **DATABASE_URL** - Set to another database url if you want to use a different database. -7. **TZ** - Set to another timezone if you want to use a different timezone. - -## Installation Script - -Here is a Bash script for installing Dokploy on a Linux server. Make sure you run this as root on a Linux environment that is not a container, and ensure ports 80, 443, and 3000 are free. +## Inspect the Installation Script +Download the installer and its published SHA-256 checksum before running it: ```bash -#!/bin/bash -install_dokploy() { - if [ "$(id -u)" != "0" ]; then - echo "This script must be run as root" >&2 - exit 1 - fi - - # check if is Mac OS - if [ "$(uname)" = "Darwin" ]; then - echo "This script must be run on Linux" >&2 - exit 1 - fi - - # check if is running inside a container - if [ -f /.dockerenv ]; then - echo "This script must be run on Linux" >&2 - exit 1 - fi - - # check if something is running on port 80 - if ss -tulnp | grep ':80 ' >/dev/null; then - echo "Error: something is already running on port 80" >&2 - exit 1 - fi - - # check if something is running on port 443 - if ss -tulnp | grep ':443 ' >/dev/null; then - echo "Error: something is already running on port 443" >&2 - exit 1 - fi - - # check if something is running on port 3000 - if ss -tulnp | grep ':3000 ' >/dev/null; then - echo "Error: something is already running on port 3000" >&2 - echo "Dokploy requires port 3000 to be available. Please stop any service using this port." >&2 - exit 1 - fi - - command_exists() { - command -v "$@" > /dev/null 2>&1 - } - - if command_exists docker; then - echo "Docker already installed" - else - curl -sSL https://get.docker.com | sh - fi - - docker swarm leave --force 2>/dev/null - - get_ip() { - local ip="" - - # Try IPv4 first - # 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 - 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=" >&2 - exit 1 - fi - - echo "$ip" - } - - advertise_addr="${ADVERTISE_ADDR:-$(get_ip)}" - echo "Using advertise address: $advertise_addr" - - docker swarm init --advertise-addr $advertise_addr - - if [ $? -ne 0 ]; then - echo "Error: Failed to initialize Docker Swarm" >&2 - exit 1 - fi - - echo "Swarm initialized" - - docker network rm -f dokploy-network 2>/dev/null - docker network create --driver overlay --attachable dokploy-network - - echo "Network created" - - mkdir -p /etc/dokploy - - chmod 777 /etc/dokploy - - # Generate secure random password for Postgres - POSTGRES_PASSWORD=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-32) - - # Store password as Docker Secret (encrypted and secure) - echo "$POSTGRES_PASSWORD" | docker secret create dokploy_postgres_password - 2>/dev/null || true - - echo "Generated secure database credentials (stored in Docker Secrets)" - - docker service create \ - --name dokploy-postgres \ - --constraint 'node.role==manager' \ - --network dokploy-network \ - --env POSTGRES_USER=dokploy \ - --env POSTGRES_DB=dokploy \ - --secret source=dokploy_postgres_password,target=/run/secrets/postgres_password \ - --env POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password \ - --mount type=volume,source=dokploy-postgres,target=/var/lib/postgresql/data \ - postgres:16 - - # Installation - docker service create \ - --name dokploy \ - --replicas 1 \ - --network dokploy-network \ - --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \ - --mount type=bind,source=/etc/dokploy,target=/etc/dokploy \ - --mount type=volume,source=dokploy,target=/root/.docker \ - --secret source=dokploy_postgres_password,target=/run/secrets/postgres_password \ - --publish published=3000,target=3000,mode=host \ - --update-parallelism 1 \ - --update-order stop-first \ - --constraint 'node.role == manager' \ - -e POSTGRES_PASSWORD_FILE=/run/secrets/postgres_password \ - dokploy/dokploy:latest - - - 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:ro \ - -p 80:80/tcp \ - -p 443:443/tcp \ - -p 443:443/udp \ - traefik:v3.6.7 - - docker network connect dokploy-network dokploy-traefik - - - # Optional: Use docker service create instead of docker run - # docker service create \ - # --name dokploy-traefik \ - # --constraint 'node.role==manager' \ - # --network dokploy-network \ - # --mount type=bind,source=/etc/dokploy/traefik/traefik.yml,target=/etc/traefik/traefik.yml \ - # --mount type=bind,source=/etc/dokploy/traefik/dynamic,target=/etc/dokploy/traefik/dynamic \ - # --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock,readonly \ - # --publish mode=host,published=443,target=443 \ - # --publish mode=host,published=80,target=80 \ - # --publish mode=host,published=443,target=443,protocol=udp \ - # traefik:v3.6.7 - - GREEN="\033[0;32m" - YELLOW="\033[1;33m" - BLUE="\033[0;34m" - NC="\033[0m" # No Color - - format_ip_for_url() { - local ip="$1" - if echo "$ip" | grep -q ':'; then - # IPv6 - echo "[${ip}]" - else - # IPv4 - echo "${ip}" - fi - } - - formatted_addr=$(format_ip_for_url "$advertise_addr") - echo "" - printf "${GREEN}Congratulations, Dokploy is installed!${NC}\n" - printf "${BLUE}Wait 15 seconds for the server to start${NC}\n" - printf "${YELLOW}Please go to http://${formatted_addr}:3000${NC}\n\n" -} - -update_dokploy() { - echo "Updating Dokploy..." - - # Pull the latest image - docker pull dokploy/dokploy:latest - - # Update the service - docker service update --image dokploy/dokploy:latest dokploy - - echo "Dokploy has been updated to the latest version." -} - -# Main script execution -if [ "$1" = "update" ]; then - update_dokploy -else - install_dokploy -fi - +curl --fail --show-error --location --retry 5 \ + --output install.sh https://dokploy.com/install.sh +curl --fail --show-error --location --retry 5 \ + --output install.sh.sha256 https://dokploy.com/install.sh.sha256 +sha256sum --check install.sh.sha256 +less install.sh +sudo bash install.sh ``` -This script includes checks for common pitfalls, installs Docker if it’s not already installed, initializes a Docker Swarm, creates a network, and then pulls and deploys Dokploy. After the script runs, it provides a success message and instructions for accessing Dokploy. +The installer requires Bash 4.3 or newer. It performs a preflight before changing +the host, preserves existing state, and writes a persistent installation log. +Use `DOKPLOY_INSTALL_ACTION=check` to inspect a host without installing packages, +writing files, restarting services, creating Docker resources, or pulling images. + +The remainder of this guide documents the individual Docker resources for +operators who intentionally need to provision or integrate them by hand. Manual +changes bypass the installer's state detection, backups, verified image digests, +rollback rules, and health validation, so test them before using them in +production. -This structured format clearly lays out the prerequisites, steps, and post-installation information, making it user-friendly and accessible for those performing manual installations. +## Customize Installation -## Customize install -#### Customize swarm advertise address +### Customize the Swarm Advertise Address The --advertise-addr parameter in the docker swarm init command specifies the IP address or interface that the Docker Swarm manager node should advertise to other nodes in the Swarm. This address is used by other nodes to communicate with the manager. By default, the script auto-detects the server IP address (the installer at `dokploy.com/install.sh` prefers a private RFC1918 address and falls back to the public IP). However, you might need to customize this address based on your network configuration, especially if your server has multiple network interfaces or if you're setting up Swarm in a private network. -To customize it, set the `ADVERTISE_ADDR` environment variable when running the script: +To customize it, set `DOKPLOY_ADVERTISE_ADDR` when running the script: ```bash -curl -sSL https://dokploy.com/install.sh | sudo ADVERTISE_ADDR=192.168.1.100 sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_ADVERTISE_ADDR=192.168.1.100 bash ``` :warning: This IP address should be accessible to all nodes that will join the Swarm. @@ -262,7 +53,9 @@ curl -sSL https://dokploy.com/install.sh | sudo ADVERTISE_ADDR=192.168.1.100 sh ## Proxmox LXC Considerations -If you're installing Dokploy in a Proxmox LXC container, the installation script automatically detects the environment and adds `--endpoint-mode dnsrr` to Docker services for compatibility. +Proxmox LXC is experimental. The installer requires +`DOKPLOY_ALLOW_UNSUPPORTED=1`, detects this environment, and adds +`--endpoint-mode dnsrr` to Docker services for compatibility. For manual installations in LXC, add this flag to your service creation commands: @@ -279,9 +72,14 @@ docker service create \ ## Existing Docker Swarm -If you already have a Docker Swarm running on your server, do **not** use the installation script — it forces the node to leave the current swarm (`docker swarm leave --force`) and re-initializes it, which would disrupt your existing services. +The installer never forces a host to leave an existing Swarm. By default it +stops when a non-Dokploy Swarm is detected. To install on an existing manager +after reviewing the resource names and port requirements, set +`DOKPLOY_REUSE_EXISTING_SWARM=1`. The manual steps below show the equivalent +resource topology. -Instead, run the following steps on a **manager** node. They replicate exactly what the install script does, minus the swarm initialization: +To provision the resources yourself, run the following steps on a **manager** +node. They show the same resource topology without Swarm initialization: ```bash # 1. Create the network @@ -289,7 +87,7 @@ docker network create --driver overlay --attachable dokploy-network # 2. Create the config directory mkdir -p /etc/dokploy -chmod 777 /etc/dokploy +chmod 700 /etc/dokploy # 3. Create the secrets (Postgres password + auth secret) openssl rand -base64 32 | tr -d "=+/" | cut -c1-32 | docker secret create dokploy_postgres_password - @@ -369,57 +167,66 @@ docker service update --env-add TZ=America/New_York dokploy To upgrade Dokploy manually, you can use the following command: ```bash -curl -sSL https://dokploy.com/install.sh | sh -s update +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_INSTALL_ACTION=upgrade bash ``` ### Version-Specific Installation & Updates The installation script automatically detects the latest stable version from GitHub. You can also specify a particular version: -**Install a Specific Version (Recommended):** +**Install or Upgrade to a Specific Version:** -Every [Dokploy release](https://github.com/Dokploy/dokploy/releases) includes its own `install.sh` as a release asset, which installs exactly that version with the setup it expects. Just replace the version tag in the URL with the one you want: +Pin the canonical installer to an immutable release tag: ```bash -curl -sL https://github.com/Dokploy/dokploy/releases/download/v0.15.0/install.sh | sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_VERSION=v0.29.13 bash ``` - This is the recommended way to install older versions of Dokploy. The main script at `dokploy.com/install.sh` always targets the latest release, so its setup may not be compatible with older versions. + For an existing installation, also set `DOKPLOY_INSTALL_ACTION=upgrade`. + Major-version transitions and downgrades require + `DOKPLOY_ALLOW_BREAKING_UPGRADE=1`. **Install/Update to Canary (Development):** ```bash -export DOKPLOY_VERSION=canary && curl -sSL https://dokploy.com/install.sh | sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_VERSION=canary bash ``` **Install/Update to Latest Stable:** ```bash -export DOKPLOY_VERSION=latest && curl -sSL https://dokploy.com/install.sh | sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_VERSION=latest bash ``` **Auto-detect Latest Stable (Default):** ```bash -curl -sSL https://dokploy.com/install.sh | sh +curl --fail --show-error --location https://dokploy.com/install.sh | sudo bash ``` -Alternatively, you can use `bash -s` for inline version specification: +Alternatively, download the script first and pass the version directly to +`sudo env`: ```bash -DOKPLOY_VERSION=canary bash -s < <(curl -sSL https://dokploy.com/install.sh) +curl --fail --show-error --location --output install.sh https://dokploy.com/install.sh +sudo env DOKPLOY_VERSION=canary bash install.sh ``` ### Additional Environment Variables **Custom Docker Swarm Network Configuration:** ```bash -export DOCKER_SWARM_INIT_ARGS="--default-addr-pool 172.20.0.0/16 --default-addr-pool-mask-length 24" -curl -sSL https://dokploy.com/install.sh | sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOCKER_SWARM_INIT_ARGS="--default-addr-pool 172.20.0.0/16 --default-addr-pool-mask-length 24" bash ``` **Manual Advertise Address:** ```bash -curl -sSL https://dokploy.com/install.sh | sudo ADVERTISE_ADDR=192.168.1.100 sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_ADVERTISE_ADDR=192.168.1.100 bash ``` Note: if you run the installer with `sudo`, pass the variable inline as shown above. Using `export` beforehand won't work because `sudo` resets the environment before executing the script. diff --git a/apps/docs/content/docs/core/troubleshooting/networking.mdx b/apps/docs/content/docs/core/troubleshooting/networking.mdx index 7f23435b..91727384 100644 --- a/apps/docs/content/docs/core/troubleshooting/networking.mdx +++ b/apps/docs/content/docs/core/troubleshooting/networking.mdx @@ -15,10 +15,13 @@ To fix this, you need to assign a the public IP address to the Docker Swarm, ide will need to use a public IP address. ```bash -curl -sSL https://dokploy.com/install.sh | sudo ADVERTISE_ADDR=your-ip sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_ADVERTISE_ADDR=your-ip bash ``` -Note: pass `ADVERTISE_ADDR` inline as shown above — if you `export` it in your shell and then run the script with `sudo`, the variable won't reach the script because `sudo` resets the environment. +Pass `DOKPLOY_ADVERTISE_ADDR` inline as shown above. If you export it in your +shell and then run the script with `sudo`, the variable normally will not reach +the installer because `sudo` resets the environment. ## Services Can't Reach Each Other (Missing IPVS Kernel Modules) @@ -63,10 +66,12 @@ If `modprobe ip_vs` fails or the config options are missing (not set to `y` or ` If you can't switch to a kernel with IPVS support, run Dokploy's services in **DNSRR mode** (DNS round-robin), which bypasses the IPVS-based load balancer entirely. -**For a fresh installation**, pass `ENDPOINT_MODE=dnsrr` to the install script: +**For a fresh installation**, pass `DOKPLOY_ENDPOINT_MODE=dnsrr` to the installer. +Systems without the supported kernel capabilities are experimental: ```bash -curl -sSL https://dokploy.com/install.sh | sudo ENDPOINT_MODE=dnsrr sh +curl --fail --show-error --location https://dokploy.com/install.sh | + sudo env DOKPLOY_ENDPOINT_MODE=dnsrr DOKPLOY_ALLOW_UNSUPPORTED=1 bash ``` **For an existing installation**, switch the Dokploy services to DNSRR mode: diff --git a/apps/website/app/self-hosted-paas/page.tsx b/apps/website/app/self-hosted-paas/page.tsx index 9400e92e..78d221aa 100644 --- a/apps/website/app/self-hosted-paas/page.tsx +++ b/apps/website/app/self-hosted-paas/page.tsx @@ -8,26 +8,26 @@ import { import AnimatedGridPattern from "@/components/ui/animated-grid-pattern"; import { Button } from "@/components/ui/button"; import { - Wallet, - Unlock, - Lock, - Settings2, + Bot, + CheckCircle2, + Cloud, + Cpu, + GitBranch, KeyRound, - ScrollText, - Shield, LifeBuoy, - ServerCog, - TerminalSquare, + Lock, MonitorSmartphone, - GitBranch, - CheckCircle2, - Cloud, + ScrollText, Server, - Bot, - Cpu, + ServerCog, + Settings2, + Shield, + TerminalSquare, + Unlock, + Wallet, } from "lucide-react"; -import Link from "next/link"; import type { Metadata } from "next"; +import Link from "next/link"; export const metadata: Metadata = { title: "The Self-Hosted PaaS Built for Developers", @@ -242,7 +242,8 @@ const faqs = [ }, ]; -const INSTALL_COMMAND = "curl -sSL https://dokploy.com/install.sh | sh"; +const INSTALL_COMMAND = + "curl --fail --show-error --location https://dokploy.com/install.sh | sudo bash"; export default function SelfHostedPaasPage() { return ( @@ -283,7 +284,11 @@ export default function SelfHostedPaasPage() { className="rounded-full bg-[#5965F2] hover:bg-[#4A55E0]" asChild > - + Contact Us @@ -353,7 +358,9 @@ export default function SelfHostedPaasPage() {

@@ -427,9 +434,9 @@ export default function SelfHostedPaasPage() { Should you choose Cloud or Self-Hosted?

- Both are great options. Every key feature available in - Self-Hosted is also available in Cloud, and vice versa. The choice - comes down to how much you want to manage yourself. + Both are great options. Every key feature available in Self-Hosted + is also available in Cloud, and vice versa. The choice comes down + to how much you want to manage yourself.

diff --git a/apps/website/components/Hero.tsx b/apps/website/components/Hero.tsx index 289a5b4d..97b67cf7 100644 --- a/apps/website/components/Hero.tsx +++ b/apps/website/components/Hero.tsx @@ -104,8 +104,9 @@ export function Hero() { animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3, delay: 0.2 }} > - Manage containerized deployments and AI-built apps across multiple servers with ease, - thanks to our all-in-one platform for developers. + Manage containerized deployments and AI-built apps across multiple + servers with ease, thanks to our all-in-one platform for + developers.
- curl -sSL https://dokploy.com/install.sh | sh + curl --fail --show-error --location + https://dokploy.com/install.sh | sudo bash