> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arct.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Install Portainer on a VPS

> Deploy Portainer Community Edition on an Arct Cloud Linux VPS with Docker, a domain, HTTPS, backups, and a safe update workflow.

Arct Cloud provides an unmanaged Linux VPS. Portainer is not preinstalled or managed by Arct Cloud; you are responsible for installation, security, backups, updates, and the containers it controls.

This guide installs Portainer Community Edition (CE) on Ubuntu 24.04 using Portainer's supported Docker Compose method. Docker officially supports Ubuntu 24.04.

## Requirements

| Basis                             | CPU and memory                                                                | Software and storage                                                                                              |
| --------------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| **Portainer requirements**        | Portainer does not publish a numeric CPU or RAM minimum for Docker Standalone | A currently supported Docker version, `sudo` access, a supported architecture, and persistent storage for `/data` |
| **Practical Arct recommendation** | 2 vCPUs and 4 GB RAM (`cvm.micro`)                                            | 40 GB NVMe for Portainer and a small number of light containers; size for the workloads Portainer will manage     |

Portainer validates specific Docker versions for each release. Check the [current compatibility table](https://docs.portainer.io/start/requirements-and-prerequisites) before upgrading Docker or Portainer.

## Install Portainer

<Steps>
  <Step title="Deploy Ubuntu 24.04">
    [Deploy a server](/compute/virtual-machines/deploy), select a plan that meets your workload requirements, and choose Ubuntu 24.04.
  </Step>

  <Step title="Connect over SSH">
    Find the server IP address in the Arct Cloud console, then connect:

    ```bash theme={null}
    ssh ubuntu@YOUR_SERVER_IP
    ```

    See [Connect via SSH](/compute/virtual-machines/connect-ssh) for key and username help.
  </Step>

  <Step title="Point a domain to the server">
    Create an `A` record such as `portainer.example.com` that points to the server's public IPv4 address. Add an `AAAA` record only if IPv6 is configured on the server. Wait until the record resolves before requesting a certificate:

    ```bash theme={null}
    getent ahostsv4 portainer.example.com
    ```
  </Step>

  <Step title="Install Docker Engine">
    Install Docker from Docker's official Ubuntu repository. Do not install Docker through Snap; Portainer warns that the Snap package can cause compatibility issues.

    ```bash theme={null}
    sudo apt update
    sudo apt install -y ca-certificates curl
    sudo install -m 0755 -d /etc/apt/keyrings
    sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
      -o /etc/apt/keyrings/docker.asc
    sudo chmod a+r /etc/apt/keyrings/docker.asc

    sudo tee /etc/apt/sources.list.d/docker.sources >/dev/null <<EOF
    Types: deb
    URIs: https://download.docker.com/linux/ubuntu
    Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
    Components: stable
    Architectures: $(dpkg --print-architecture)
    Signed-By: /etc/apt/keyrings/docker.asc
    EOF

    sudo apt update
    sudo apt install -y docker-ce docker-ce-cli containerd.io \
      docker-buildx-plugin docker-compose-plugin
    sudo systemctl enable --now docker
    sudo docker run --rm hello-world
    ```
  </Step>

  <Step title="Create the Portainer Compose project">
    The `lts` tag follows Portainer's supported long-term-support channel. The UI is bound to loopback so it is reachable only from the VPS or through an SSH tunnel until you explicitly enable the HTTPS reverse proxy. Port `8000` is intentionally omitted because a local Docker environment does not need the Edge Agent tunnel.

    ```bash theme={null}
    sudo install -d -m 750 -o "$USER" -g "$USER" /opt/portainer
    cd /opt/portainer

    tee compose.yaml >/dev/null <<'EOF'
    services:
      portainer:
        container_name: portainer
        image: portainer/portainer-ce:lts
        restart: always
        volumes:
          - /var/run/docker.sock:/var/run/docker.sock
          - portainer_data:/data
        ports:
          - "127.0.0.1:9443:9443"

    volumes:
      portainer_data:
        name: portainer_data
    EOF

    sudo docker compose config
    ```
  </Step>

  <Step title="Start Portainer and claim the admin account privately">
    Start the container only when you are ready to complete setup:

    ```bash theme={null}
    cd /opt/portainer
    sudo docker compose up -d
    ```

    From a **second terminal on your local computer**, open an SSH tunnel and leave it running:

    ```bash theme={null}
    ssh -N -L 9443:127.0.0.1:9443 ubuntu@YOUR_SERVER_IP
    ```

    Open `https://localhost:9443`. The certificate warning is expected because Portainer uses a self-signed certificate on this private connection. Create the first administrator with a unique password of at least 12 characters, then select the automatically detected local Docker environment. Close the tunnel with `Ctrl+C` only after setup is complete.

    <Warning>Portainer allows five minutes to create the first administrator. If setup times out, run `sudo docker restart portainer`, reconnect the SSH tunnel, and complete setup within the next five minutes.</Warning>
  </Step>

  <Step title="Configure the public reverse proxy and HTTPS">
    Replace `portainer.example.com` in both commands with your domain. Portainer serves a self-signed certificate on its loopback-only backend; Nginx terminates the public, trusted certificate.

    ```bash theme={null}
    sudo apt install -y nginx certbot python3-certbot-nginx

    sudo tee /etc/nginx/sites-available/portainer >/dev/null <<'EOF'
    server {
        listen 80;
        listen [::]:80;
        server_name portainer.example.com;

        location / {
            proxy_pass https://127.0.0.1:9443;
            proxy_ssl_verify off;
            proxy_http_version 1.1;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_read_timeout 3600;
        }
    }
    EOF

    sudo ln -s /etc/nginx/sites-available/portainer \
      /etc/nginx/sites-enabled/portainer
    sudo nginx -t
    sudo systemctl reload nginx
    sudo certbot --nginx -d portainer.example.com
    ```

    If UFW is enabled, allow SSH before enabling only the public web ports:

    ```bash theme={null}
    sudo ufw allow OpenSSH
    sudo ufw allow 'Nginx Full'
    sudo ufw enable
    ```
  </Step>

  <Step title="Verify the deployment">
    Confirm that the container, loopback backend, and public endpoint respond:

    ```bash theme={null}
    cd /opt/portainer
    sudo docker compose ps
    curl -kI https://127.0.0.1:9443
    curl -I https://portainer.example.com
    sudo docker logs --tail 50 portainer
    ```
  </Step>
</Steps>

## Persistent Data and Security

The `portainer_data` volume contains Portainer's database and configuration. Registry credentials, endpoint configuration, and other sensitive settings stored by Portainer must be treated as secrets. Keep any downloaded backup encrypted and outside the VPS.

<Warning>Mounting `/var/run/docker.sock` gives Portainer control equivalent to root over this Docker host. Limit administrator access, use a unique password, and never publish the Docker API on ports `2375` or `2376` for this setup.</Warning>

Only ports `80` and `443` need to be publicly reachable. Port `9443` is bound to `127.0.0.1`; legacy HTTP port `9000` is not enabled. Port `8000` is needed only for Portainer Edge Agents. If you later enable Edge features, follow Portainer's current networking guidance and restrict exposure wherever possible.

Docker-published ports can bypass UFW rules. Keeping the Portainer mapping on loopback prevents direct access even when Docker manages its own firewall rules.

## Back Up and Restore

1. In Portainer, open **Settings** and find **Back up Portainer**.
2. Enable password protection, use a unique backup password, and download the `tar.gz` archive.
3. Store the archive and its password separately from the VPS.
4. Test restoration on a fresh Portainer instance with an empty data volume. On the initialization screen, expand **Restore Portainer from backup** and select the archive.

<Note>A Portainer configuration backup covers data in Portainer's `/data` volume. It does not back up the containers, Compose files, application volumes, or databases that Portainer manages. Back up those workloads independently.</Note>

## Update Safely

Use the LTS channel for routine production updates:

1. Read the [release notes](https://github.com/portainer/portainer/releases) and compatibility matrix.

2. Download an encrypted Portainer configuration backup and back up every managed workload separately.

3. Record the current image digest:

   ```bash theme={null}
   sudo docker image inspect portainer/portainer-ce:lts \
     --format '{{index .RepoDigests 0}}'
   ```

4. Pull and recreate the container, preserving `portainer_data`:

   ```bash theme={null}
   cd /opt/portainer
   sudo docker compose pull
   sudo docker compose up -d
   sudo docker compose ps
   sudo docker logs --tail 100 portainer
   ```

5. Confirm login and local environment access before pruning old images.

Portainer can migrate its database during an update. Do not point an older image at a migrated `portainer_data` volume. To roll back, deploy the previous supported image against a fresh empty volume and restore the pre-update Portainer backup during initialization.

## Troubleshooting

<AccordionGroup>
  <Accordion title="The public URL returns 502 Bad Gateway">
    Check `sudo docker compose -f /opt/portainer/compose.yaml ps` and `sudo docker logs --tail 100 portainer`. Confirm that `curl -kI https://127.0.0.1:9443` succeeds, then run `sudo nginx -t`.
  </Accordion>

  <Accordion title="Portainer says the instance timed out">
    Restart it with `sudo docker restart portainer`, reload the page, and create the administrator within five minutes. Do not delete the data volume.
  </Accordion>

  <Accordion title="The browser reports a certificate or domain error">
    Verify that the DNS `A` record resolves to this VPS, ports `80` and `443` are reachable, and `sudo certbot certificates` lists the expected domain. Do not browse directly to port `9443`; its backend certificate is self-signed.
  </Accordion>

  <Accordion title="The local Docker environment is unavailable">
    Confirm the socket mount with `sudo docker inspect portainer --format '{{json .Mounts}}'` and check that Docker is running with `sudo systemctl status docker`. Rootless Docker needs separate Portainer configuration and has limitations.
  </Accordion>
</AccordionGroup>

## Official Resources

<CardGroup cols={3}>
  <Card title="Portainer Documentation" icon="book-open" href="https://docs.portainer.io/start/install-ce/server/docker/linux">
    Official Docker Standalone installation and networking guidance.
  </Card>

  <Card title="Portainer on GitHub" icon="github" href="https://github.com/portainer/portainer">
    Source code, security policy, and issue tracker.
  </Card>

  <Card title="Portainer Releases" icon="tag" href="https://github.com/portainer/portainer/releases">
    LTS releases, fixes, and upgrade notes.
  </Card>
</CardGroup>

<Note>Portainer is developed by Portainer.io. Arct Cloud is an independent infrastructure provider and is not affiliated with, sponsored by, or endorsed by Portainer.io.</Note>
