Pracandy
← Newsroom

Features

Deploying to a VPS From GitHub Actions Without a Platform

Deploying to a VPS with GitHub Actions replaces the hosting dashboard with a script that runs on your own infrastructure.

22 October 2026

Ask AI about this story

Mid 20th century brass padlock model 3030 by Abloy Finland
Photo: R. Henrik Nilsson, CC BY 4.0, via Wikimedia Commons

The pipeline in practice

Deploying to a VPS with GitHub Actions replaces the hosting dashboard with a script that runs on your own infrastructure. The process starts when code is pushed to the main branch, triggering a build job that compiles the application and packages it into a single archive.

That archive is then transferred to the server via SSH. On the remote machine, a simple shell script unpacks the files, installs dependencies if needed, and restarts the relevant service. This direct approach means the server state is always exactly what the last successful build produced, without an intermediary layer caching or transforming the code.

Managing secrets and keys

Security in this setup relies on least privilege. You generate an SSH key pair specifically for deployment, adding the public key to the server’s authorized keys. The private key is stored as a secret in your GitHub repository, ensuring it never appears in logs or source code.

  • Restrict the key to specific commands using the command option in authorized_keys.
  • Disable password authentication entirely to prevent brute-force attempts.
  • Use a dedicated user account for the deploy key rather than root.

This isolation limits the damage if a key is compromised. The deploy user can only execute the specific script defined, preventing arbitrary command execution on the host.

Münster, Domplatz, Sparschwein 2022 0863
Photo: Dietmar Rabich, CC BY-SA 4.0, via Wikimedia Commons source

Handling the server stack

Our stack runs on a single VPS with 12GB of RAM and a 193GB disk. A Next.js process serves four sites by host-based routing, while Instalaz runs as a separate Node process on port 3050. Both are proxied by Caddy, which terminates TLS and obtains certificates from Let's Encrypt automatically.

GamingCap and Invoiceful are static builds served directly from disk by Caddy, requiring no server process or database. All applications share one PostgreSQL 16 database. Scheduled tasks run as systemd oneshot services with timers, avoiding the complexity of cron. This configuration keeps resource contention manageable, as processes on one box compete for memory and CPU.

When to skip the platform

Hosting platforms provide automatic scaling, managed databases, and built-in monitoring. They abstract away the operational burden, which is valuable when you lack time to maintain infrastructure. However, they add cost and reduce control over the server environment.

Self-hosting via GitHub Actions is appropriate when your traffic is predictable and your team can handle server administration. It offers full transparency and lower running costs for small to medium workloads. If you need elastic scaling or zero-downtime deployments with complex rollbacks, a platform may still be the better choice. For our use case, the direct control outweighs the convenience of managed services. See how we manage content workflows in our PracandyFlix stories.