We have completed the development cycle. We have an optimized application, with a solid architecture… that only works on your machine.
Now comes the moment of truth. We need to build the real website and deploy it to a server so everyone can see it.
In this article, we will address Astro deployment, prioritizing the use of a Virtual Private Server (VPS), both for static sites and server-rendered applications (SSR). We will also briefly review PaaS options for rapid prototyping.
Preparing the Artifact (Build)
Regardless of the deployment destination, the first step is to generate the production code. Astro compiles our code, optimizes images, and minifies assets.
npm run build
This command will generate a dist/ folder in the project root. The content of this folder will vary depending on the mode configured in astro.config.mjs:
- Static Mode (
output: 'static'): Will contain.html,.css, and.jsfiles ready to be served by any web server. - Server Mode (
output: 'server'): Will contain the server entry point and the necessary assets to run the application in a Node.js environment (or the selected adapter).
Deployment on VPS
This is the recommended option for having full control and no third-party dependencies. We will assume a standard Linux environment (Ubuntu/Debian).
Static Site (SSG)
If your project is purely static (blog, documentation, landing page), deployment is very simple. We only need an efficient web server like Nginx or Apache to serve the dist folder.
Copy the content of dist/ to our server (e.g., via scp or a CI/CD pipeline) to the path /var/www/mi-proyecto.
server {
listen 80;
server_name midominio.com;
root /var/www/mi-proyecto;
index index.html;
location / {
try_files $uri $uri/ /404.html;
}
# Aggressive cache for immutable assets (Astro generates hashes in filenames)
location /_astro/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
SSR Application (Node.js)
If we use output: 'server' or 'hybrid', we need a runtime environment. For a standard VPS, we will use Node.js.
First, we must adapt Astro to function as an independent Node.js server.
npx astro add node
This will configure the adapter in astro.config.mjs. Make sure to use the standalone mode:
import node from '@astrojs/node';
export default defineConfig({
output: 'server',
adapter: node({
mode: 'standalone'
})
});
After running npm run build, we will have a dist/server/entry.mjs file. We can run it with node dist/server/entry.mjs, but in production we need a process manager to ensure the application restarts if it fails or if the server reboots.
We will use PM2:
# Global installation of PM2
npm install -g pm2
# Start the application
pm2 start dist/server/entry.mjs --name "mi-astro-app"
We should not expose the Node.js port (default 4321 or 8080) directly to the internet. The correct approach is to use Nginx as a Reverse Proxy to manage SSL, Gzip/Brotli compression, and security headers.
server {
listen 80;
server_name midominio.com;
location / {
proxy_pass http://localhost:4321; # Port where Astro runs
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
PaaS Alternatives (Vercel / Netlify)
If the project does not require dedicated infrastructure or we are in a rapid prototyping phase, Platform as a Service (PaaS) offerings are valid options.
These platforms abstract server management and offer integrated CI/CD (they connect to your Git repository and deploy on push).
Considerations when using PaaS
- Specific Adapters: Just like with Node.js, if you use SSR on these platforms, you must install their specific adapter (
@astrojs/vercelor@astrojs/netlify) so that Astro generates serverless functions compatible with their infrastructure. - Limits: Be aware of the execution limits (timeout) of serverless functions and bandwidth in free plans.
- Vendor Lock-in: Code generated for Vercel may not work directly on Netlify without changing the adapter and configuration.
