--- title: Deploying Astro SSR to a VPS with Podman description: A practical VPS deployment flow for Astro SSR using Podman and podman-compose. pubDate: '2026-03-02T11:24:00+01:00' category: en/on-premises-private-cloud tags: - podman - deployment seriesParent: astro-ssr-with-podman seriesOrder: 2 --- With the Astro SSR container running cleanly on my laptop, I wanted to move it onto the VPS without inventing a new set of commands for production. The goal was a short, boring deploy sequence — the same compose file, the same entry point, just a different host. This is the exact path that worked on a fresh Debian VPS. ## The setup - **VPS**: Debian, freshly provisioned, SSH access as a non-root user. - **Runtime**: Podman and `podman-compose` from the distro packages. - **Source of truth**: a Git repository cloned into `/opt/website`. ## Install runtime tools ```bash sudo apt-get update sudo apt-get -y install podman podman-compose git ``` Nothing exotic — the distro packages are modern enough for a single-container app. ## Cloning the repository **Problem:** My first `git clone` over HTTPS failed immediately with: `Invalid username or token. Password authentication is not supported` GitHub dropped password auth years ago, and the VPS had no credential helper configured. Rather than pasting a PAT onto the server, I switched to key-based SSH. **Implementation:** ```bash mkdir -p ~/.ssh && chmod 700 ~/.ssh ssh-keygen -t ed25519 -C "vps-website" -f ~/.ssh/id_ed25519 -N "" cat ~/.ssh/id_ed25519.pub ``` That public key went into the repository's Deploy Keys — read-only, scoped to this one repo, nothing else. ```bash ssh-keyscan github.com >> ~/.ssh/known_hosts git clone git@github.com:adrian-altner/website.git /opt/website ``` **Solution:** `git pull` now works unattended — no PAT expiry, no prompts, no shared account credentials on the box. ## Starting the app ```bash cd /opt/website podman-compose -f compose.yml up --build -d ``` Three quick checks — the container is up, the logs show Astro booting, the local port answers: ```bash podman ps podman logs -f website curl -I http://127.0.0.1:4321 ``` A `200` from the last one means the app is healthy behind the scenes. It's still on localhost only — Caddy comes in a later post. ## The gotchas I hit **Docker Hub auth errors on first build.** The initial pull can fail with: `unable to retrieve auth token: invalid username/password` Even with public images. Pulling the base image explicitly once clears whatever stale state Podman is carrying: ```bash podman pull docker.io/library/node:20-bookworm-slim ``` After that, `podman-compose up --build -d` goes through cleanly. **Compose provider warnings.** If Podman announces it's falling back to an external `docker-compose`, installing and pinning `podman-compose` takes the ambiguity out: ```bash sudo apt-get -y install podman-compose export PODMAN_COMPOSE_PROVIDER=/usr/bin/podman-compose ``` From then on: ```bash podman compose -f compose.yml up --build -d ``` ## What to take away - Key-based SSH with a Deploy Key beats HTTPS + PAT for unattended pulls — no expiry to manage. - The local `compose.yml` transfers to the VPS unchanged; that's the whole point. - A stale Podman auth cache is the most likely cause of mysterious first-pull failures — pull the base image directly once to reset it. - Pin `PODMAN_COMPOSE_PROVIDER` explicitly on servers so the command can't silently change behaviour under you.