Skip to content
DasPeTe Tutorials

Your Portfolio With Manablox · Part 7 of 7 in the series

Part 7: Put Your Portfolio Online

Rent a server, point your domain at it, and run the CMS, the public API and your Astro site there with Docker and automatic HTTPS.

Quest objectives

  • Know what runs where on a production server
  • Point a domain at a server with DNS records
  • Package the Astro site as a Docker image
  • Start everything with Docker Compose and Caddy
  • Move your content from your computer to the server

Your portfolio works on your computer. Now it moves to a server, so everyone can reach it. On the server, six programs run side by side, each in its own Docker container:

  • the CMS with the admin, at cms.robin.example,

  • the public API, at api.robin.example,

  • your Astro site, at robin.example,

  • the site process from part 2, in case you switch back to a designed site,

  • Valkey, the cache from part 1,

  • and Caddy, a web server in front of all of them. It sends every visitor to the right program and gets HTTPS certificates by itself, so your site has the lock in the address bar.

Replace robin.example with your domain and 203.0.113.10 with the address of your server everywhere in this part.

What you need

  • A server: a small virtual server (VPS) with Ubuntu 24.04 and at least 2 GB of memory. Every hosting company offers one, for a few euros a month. You get its IP address and a password or SSH key for the user root.

  • A domain, such as robin.example, from any domain registrar.

Point your domain at the server

DNS is the internet's address book: it turns robin.example into the IP address of your server. In the DNS settings of your registrar, add three A records:

text
Type   Name                Value
A      robin.example       203.0.113.10
A      cms.robin.example   203.0.113.10
A      api.robin.example   203.0.113.10

Create the server project

The project from part 1 runs the CMS on your computer with pnpm dev. For the server, the CLI writes a second project in which everything runs in containers. Run this on your computer, in your projects folder, with your domain and email address:

~/projects
$ pnpm dlx @manablox/cli create portfolio-server --preset docker --proxy caddy --database sqlite --website --admin-domain cms.robin.example --public-domain api.robin.example --acme-email robin@example.com --yesWriting portfolio-serverWrote 19 files to portfolio-serverInstalling dependencies with pnpmDependencies installedInitialised a git repository
  • --preset docker puts every process into a container, with a Dockerfile and a compose.yml that ties them together.

  • --proxy caddy puts Caddy in front, with automatic certificates.

  • --admin-domain and --public-domain are the addresses of the admin and the public API. --acme-email is where the certificate authority, Let's Encrypt, writes if a certificate ever needs your attention.

  • --database sqlite and --website match your project from part 1, so your content fits in.

The secrets in portfolio-server/.env were generated fresh for the server. Never share that file.

Add your Astro site

Copy the site into the server project, as the folder web, without the installed packages and the old build:

~/projects
$ cp -r my-site portfolio-server/web$ rm -rf portfolio-server/web/node_modules portfolio-server/web/dist portfolio-server/web/.git

A Dockerfile is the recipe for a container image. This one builds your site in a first step and copies only what it needs to run into a second, smaller image. Create web/Dockerfile:

portfolio-server/web/Dockerfile
# The portfolio: build the Astro site, then run its Node server.
FROM node:24-bookworm-slim AS build
WORKDIR /app
RUN corepack enable
ENV CI=true
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
COPY . .
RUN pnpm build

FROM node:24-bookworm-slim
WORKDIR /app
ENV NODE_ENV=production HOST=0.0.0.0 PORT=3005
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY --from=build /app/package.json ./
USER node
EXPOSE 3005
CMD ["node", "./dist/server/entry.mjs"]

The .dockerignore next to it keeps the files out of the image that are made inside it anyway:

portfolio-server/web/.dockerignore
node_modules
dist
.astro
.env

The CMS image is built from the whole server folder, so tell it to leave your site out. Add one line at the end of portfolio-server/.dockerignore:

portfolio-server/.dockerignore
web

Add the site to Docker Compose

compose.yml lists the containers. Add your site as a new service, right above caddy:. Inside the compose network, the containers reach each other by their service names, so the site reads from http://public:3100:

portfolio-server/compose.yml
# The portfolio itself: the Astro site in web/. It reads the content from the public
  # API over the compose network.
  web:
    build: ./web
    restart: unless-stopped
    <<: *logging
    depends_on:
      - public
    environment:
      MANABLOX_URL: http://public:3100
      MANABLOX_ADMIN_ORIGIN: ${PUBLIC_URL:?set PUBLIC_URL}

In the caddy service below it, add web to depends_on and pass the new domain on:

portfolio-server/compose.yml
caddy:
    # ...
    depends_on:
      - api
      - public
      - web
      # ...
    environment:
      ADMIN_DOMAIN: ${ADMIN_DOMAIN:?set ADMIN_DOMAIN}
      SITE_DOMAIN: ${SITE_DOMAIN:?set SITE_DOMAIN}
      # ...

Then tell Caddy where your site lives. In caddy/Caddyfile, add this block above the line # manablox:slot caddy.sites:

portfolio-server/caddy/Caddyfile
# The portfolio, the Astro site in web/.
{$SITE_DOMAIN} {
	import hardening
	reverse_proxy web:3005
}

And give the domain a value in .env, next to PUBLIC_DOMAIN:

portfolio-server/.env
# The portfolio, the Astro site in web/.
SITE_DOMAIN=robin.example

Upload and start

rsync copies the folder to the server and leaves out what the images build themselves:

~/projects
$ rsync -av --exclude node_modules --exclude dist --exclude .git portfolio-server/ root@203.0.113.10:/opt/portfolio/

Then log in to the server with ssh, install Docker with its official script, and start everything. The first build takes a few minutes.

~/projects
$ ssh root@203.0.113.10$ curl -fsSL https://get.docker.com | sh$ cd /opt/portfolio$ docker compose up -d --build Image portfolio-server:latest Built Image portfolio-server-web Built Container portfolio-server-valkey-1 Healthy Container portfolio-server-api-1 Started Container portfolio-server-public-1 Started Container portfolio-server-site-1 Started Container portfolio-server-web-1 Started Container portfolio-server-caddy-1 Started

Move your content

The server has an empty CMS. Your content is on your computer, so you take it along as an export. In your local admin at localhost:3000, open Settings → Export and transfer, choose Zip archive, which also carries the pictures, and click Download export.

The zip archive holds everything in the space, the pictures included.Open the screenshot in full size
The zip archive holds everything in the space, the pictures included.

Now open https://cms.robin.example. The server greets you with the same setup assistant as in part 1. Create your account, with a new, strong password, and then choose Import a space instead of creating one.

Import a space restores an export.Open the screenshot in full size
Import a space restores an export.
Pick the zip file and import it.Open the screenshot in full size
Pick the zip file and import it.

The import still has the website address from your computer. Open Settings, change the Website address to https://robin.example and save. The Visual button then frames the live site.

The website address of the space on the server.Open the screenshot in full size
The website address of the space on the server.

Online

Open https://robin.example. Your portfolio is on the internet, with HTTPS, your content and your pictures, which now come from api.robin.example. Share the link, and the previews show your covers.

Changing things later

Content you change in the admin at cms.robin.example, and it is live as soon as you publish. Your computer's CMS was only the workshop.

Code you change on your computer, in my-site. When you are happy, copy the changed files into portfolio-server/web, upload, and rebuild:

~/projects
$ rsync -av --exclude node_modules --exclude dist --exclude .git portfolio-server/ root@203.0.113.10:/opt/portfolio/$ ssh root@203.0.113.10 "cd /opt/portfolio && docker compose up -d --build"

That is the whole journey: from an empty folder to a CMS, a designed site, your own Astro frontend and a portfolio on the internet. Congratulations! Keep adding projects, and when you outgrow the blocks you have, add new block types in the admin and a component for each, just like the contact button.

Comments

The comments are GitHub Discussions: sign in with GitHub to write one.

The comments come from GitHub, through giscus. Loading them sends your IP address to giscus.app and GitHub.