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 composetriesdocker-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.