This post is a quick memo about setting up an automated deployment pipeline of an Astro (or any other TypeScript/JavaScript app) website on a VPS using a Codeberg runner installed on the VPS.
The pipeline will look like this:
Laptop
│
│ git push
▼
Codeberg
│
▼
Forgejo Runner (VPS)
│
├── npm ci
├── npm run build
└── rsync dist/ → /var/www/blog
│
▼
Caddy
│
▼
Visitors
Install Forgejo runner on the VPS (Rocky Linux)↗
Codeberg uses Forgejo, so the first task is to install the Forgejo runner on the server. The runner listens for pushes to main branch from the Codeberg repository and executes the defined actions.
- Create a dedicated user
sudo useradd --system --create-home --shell /bin/bash forgejo-runner
- —system : Create a system user (no password, no interactive login by default).
- —create-home : Create the home directory
/home/forgejo-runner. - —shell /bin/bash : Bash shell.
- Login as the new user
sudo su - forgejo-runner
The flag - loads the environment of the user (variables HOME, PATH, etc.).
- Dowload of the binary from Codeberg
Get the lastest version of the runner binary from Codeberg - Forgejo Runner Releases. Example for the current latest version as of 20260720:
cd ~
mkdir -p actions-runner && cd actions-runner
wget https://code.forgejo.org/forgejo/runner/releases/download/v12.13.1/forgejo-runner-12.13.1-linux-amd64
chmod +x forgejo-runner-12.13.1-linux-amd64
ln -s forgejo-runner-12.13.1-linux-amd64 act_runner
Configure and test the runner↗
Before configuring the runner, register a new runner with Codeberg. On Codeberg, navigate to:
Repository
→ Settings
→ Actions
→ Runners
→ Create new runner
After registering, you’ll receive a token and a UUID. Use these in the configuration file below.
Create a config.yaml file in ~/actions-runner :
server:
connections:
forgejo:
url: https://codeberg.org/
uuid: aaaa-bbbb-cccc-dddd-111222333
token: 731cd04efbe70d237cb0be70d237cb0be70d2
labels:
- linux:host
- self-hosted:host
For a simple site deployment, a Docker-based runner isn’t necessary. Instead, we use rsync to copy the built files to the website’s root directory. The :host label suffix tells the runner to execute tasks directly on the VPS rather than in a Docker container.
Launch the runner (mode daemon) in test mode:↗
./act_runner daemon --config config.yaml
If everything runs without errors, proceed to make it permanent.
Create a proper systemd service↗
To ensure the runner starts automatically at boot, create a systemd service file at /etc/systemd/system/forgejo-runner.service:
[Unit]
Description=Forgejo Actions Runner
After=network.target
[Service]
User=forgejo-runner
WorkingDirectory=/home/forgejo-runner/actions-runner
Environment="PATH=/home/forgejo-runner/.nvm/versions/node/v22.23.1/bin:/usr/local/bin:/usr/bin:/bin"
ExecStart=/home/forgejo-runner/actions-runner/forgejo-runner-12.13.1-linux-amd64 daemon --config /home/forgejo-runner/actions-runner/config.yaml
Restart=always
RestartSec=5s
[Install]
WantedBy=multi-user.target
Activate and start the service:
sudo systemctl daemon-reload
sudo systemctl enable --now forgejo-runner
# Check the status
sudo systemctl status forgejo-runner
# In case of any problems, check the logs
journalctl -u forgejo-runner -f
Note: SELinux may cause issues. Apply the correct context to the runner binary with:
sudo chcon -t bin_t /home/forgejo-runner/actions-runner/forgejo-runner-12.13.1-linux-amd64
bin_tis the context for executable binaries.
The runner should no appear as online in Codeberg (under Settings > Runners).
Create the site (Astro here) workflow↗
Add a .forgejo/workflows directory at the root of the project and create a YAML file named deploy.yml with the following content:
name: Deploy Astro site
on:
push:
branches:
- main
jobs:
deploy:
runs-on: self-hosted
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Build Astro site
run: npm run build
- name: Deploy static files
run: |
rsync -av --delete dist/ /var/www/blog/
For every push to the main branch, the pipeline automatically executes:
✓ Checkout repository
✓ Setup Node.js
✓ Install dependencies
✓ Build Astro site
✓ Deploy static files
Change ownership of the Caddy root directory↗
The Caddy root directory /var/www/blog must be accessible by the forgejo-runner user to allow file copying:
sudo chown -R forgejo-runner:forgejo-runner /var/www/blog/
Testing the pipeline↗
Make a change to the site, commit and push to main:
git add .
git commit -m "Add deployment workflow"
git push
In Codeberg, navigate to Repository > Actions > Running jobs to monitor the deployment status.