← Home

How to build your Website from Local Setup to VPS

This post documents a full local setup for running an Astro SSR site with Podman.

The target is simple:

  • Astro runs in SSR mode
  • The container starts from dist/server/entry.mjs
  • The app is reachable on port 4321

1. Astro SSR configuration

Use the Node adapter in standalone mode and set output: "server".

// astro.config.mjs
import { defineConfig } from "astro/config";
import mdx from "@astrojs/mdx";
import node from "@astrojs/node";

export default defineConfig({
  site: "https://adrian-altner.de",
  output: "server",
  integrations: [mdx()],
  markdown: {
    shikiConfig: {
      theme: "github-light",
    },
  },
  adapter: node({
    mode: "standalone",
  }),
});

Also add a start command:

{
  "scripts": {
    "build": "astro build",
    "start": "node dist/server/entry.mjs"
  }
}

2. Containerfile

This multi-stage build installs dependencies, builds Astro, and ships only dist in runtime.

FROM node:20-bookworm-slim AS build

WORKDIR /app

COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm install --frozen-lockfile

COPY . .
RUN pnpm run build

FROM node:20-bookworm-slim AS runtime

WORKDIR /app

ENV NODE_ENV=production
ENV HOST=0.0.0.0
ENV PORT=4321

COPY --from=build --chown=node:node /app/dist ./dist

USER node

EXPOSE 4321

CMD ["node", "dist/server/entry.mjs"]

3. Compose file

# compose.yml
services:
  website:
    build:
      context: .
      dockerfile: Containerfile
    container_name: website
    ports:
      - "4321:4321"
    environment:
      NODE_ENV: production
      HOST: 0.0.0.0
      PORT: 4321
    restart: unless-stopped

4. Local run

podman machine start
podman compose -f compose.yml up --build -d
podman ps
curl -I http://localhost:4321

Expected result: HTTP/1.1 200 OK.

5. Useful troubleshooting

  • If podman compose tries docker-compose, set: export PODMAN_COMPOSE_PROVIDER=/opt/homebrew/bin/podman-compose
  • If the Podman VM hangs during startup, restart it: podman machine stop && podman machine start

The important part is to verify the runtime path (dist/server/entry.mjs) early. Once that is stable, deployment becomes straightforward.

← Home