Host a static website on Quake AI
Host a static website on Quake AI
In this tutorial, we deploy a static website served by Nginx on a Quake AI instance. By the end, you will have a publicly accessible web page served from your own cloud server, with a public IP you can share or point a domain name to.
What you will learn:
- How to configure an instance for web hosting
- How to install and configure Nginx as a web server
- How to deploy static HTML content
- How security groups control which ports are accessible from the internet
- How to verify your website is publicly accessible
Prerequisites#
You need:
- A Quake AI account with an active project
- An SSH key pair uploaded to Quake AI (the OpenStack name is your key pair name)
- Completed the Quickstart
The PublicEphemeral network assigns a public IP to the instance at boot, so this tutorial needs no private network, router, or floating IP. We use the OpenStack CLI so you can reproduce every step from your terminal.
Follow Generate app credentials to create and download your credentials file.
Source the file to load credentials into your shell session:
source ~/path/to/openrc.shSubstitute these placeholders in the commands: YOUR_KEYPAIR_NAME for your uploaded key pair, and UBUNTU_22_IMAGE_NAME from openstack image list for Ubuntu 22.04 (names vary by region, for example Ubuntu 22.04 or Ubuntu-22.04).
Step 1: Create a security group for SSH, HTTP, and HTTPS#
Security groups filter traffic to instance ports. Without ingress rules for 22, 80, and 443, clients cannot reach SSH or Nginx from the internet. We use a dedicated group so the rules stay organized and auditable.
openstack security group create tutorial-static-web \
--description "SSH, HTTP, and HTTPS for static site tutorial"
openstack security group rule create \
--protocol tcp --dst-port 22 --remote-ip 0.0.0.0/0 \
tutorial-static-web
openstack security group rule create \
--protocol tcp --dst-port 80 --remote-ip 0.0.0.0/0 \
tutorial-static-web
openstack security group rule create \
--protocol tcp --dst-port 443 --remote-ip 0.0.0.0/0 \
tutorial-static-webWe use 0.0.0.0/0 so any client can reach the tutorial instance. In production, narrow SSH sources and put a self-managed reverse proxy in front of HTTP services.
Step 2: Launch an Ubuntu 22.04 instance#
We attach the instance to PublicEphemeral, which assigns a public IP at boot, and combine default and tutorial-static-web so baseline project behavior stays in place while our rules open the web ports.
openstack server create \
--flavor s1a.micro \
--image UBUNTU_22_IMAGE_NAME \
--network PublicEphemeral \
--key-name YOUR_KEYPAIR_NAME \
--security-group default \
--security-group tutorial-static-web \
--wait \
tutorial-web-vmThe command returns when the server reaches ACTIVE.
Step 3: Find the instance public IP#
PublicEphemeral assigns the public address at boot, so there is no floating IP to allocate. Read the address from the instance and save it for the SSH and browser checks:
INSTANCE_IP=$(openstack server show tutorial-web-vm -f value -c addresses | sed 's/.*=//')
echo "$INSTANCE_IP"Step 4: SSH into the instance#
Ubuntu cloud images use the ubuntu user. Connect with the private key that matches YOUR_KEYPAIR_NAME:
ssh -i /path/to/YOUR_PRIVATE_KEY ubuntu@$INSTANCE_IPAccept the host key the first time you connect.
Step 5: Install Nginx#
Nginx is a small, efficient choice for static files.
sudo apt update
sudo apt install -y nginxIf apt cannot resolve the archives, your subnet is missing DNS resolvers. Run the DNS check in Prerequisites before you retry.
Step 6: Add a simple HTML page#
Ubuntu's default document root is /var/www/html/. Replace the stock page:
sudo tee /var/www/html/index.html > /dev/null <<'EOF'
<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><title>Hosted on Quake AI</title></head>
<body><h1>Hello from Quake AI</h1><p>Static HTML served by Nginx.</p></body>
</html>
EOFStep 7: Verify Nginx locally on the instance#
Confirm the service and HTTP response on the instance before testing from the internet.
systemctl is-active nginx
curl -s http://127.0.0.1/ | head -n 5Expect active and your heading in the curl output.
Step 8: Open the site in your browser#
Open http://$INSTANCE_IP in a browser. If it fails, recheck security group rules, confirm the public address with openstack server show tutorial-web-vm -c addresses, and rule out local port blocking.
Step 9: (optional) self-signed HTTPS for learning#
Self-signed certificates trigger browser warnings; they only demonstrate TLS termination in Nginx before you use a public CA.
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/nginx-selfsigned.key \
-out /etc/ssl/certs/nginx-selfsigned.crt \
-subj "/CN=TUTORIAL_HOSTNAME"
sudo tee /etc/nginx/sites-available/tutorial-tls.conf > /dev/null <<'EOF'
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name _;
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
root /var/www/html;
index index.html;
}
EOF
sudo ln -sf /etc/nginx/sites-available/tutorial-tls.conf /etc/nginx/sites-enabled/tutorial-tls.conf
sudo nginx -t && sudo systemctl reload nginxVisit https://$INSTANCE_IP and accept the expected certificate warning.
Step 10: (optional) production HTTPS with Let's Encrypt#
For HTTPS with a custom domain and a trusted certificate, install Certbot after pointing your domain's DNS A record to the instance public IP:
sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.comCertbot adds HTTPS configuration to Nginx and sets up automatic certificate renewal. You will need to add an HTTPS rule (TCP port 443) to your tutorial-static-web security group before HTTPS will work, use the same openstack security group rule create pattern as Step 1.
Cloud-init alternative (zero-touch setup)#
If you want to skip Steps 4–6 and have Nginx installed automatically at first boot, save this as cloud-init-nginx.yaml on your local machine:
#cloud-config
package_update: true
packages:
- nginx
write_files:
- path: /var/www/html/index.html
content: |
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Quake AI Site</title>
</head>
<body>
<h1>Hello from Quake AI</h1>
<p>This static site is served by Nginx, configured by cloud-init.</p>
</body>
</html>
runcmd:
- systemctl enable nginx
- systemctl start nginxPass it on openstack server create with --user-data cloud-init-nginx.yaml. Cloud-init takes 1–2 minutes after boot to finish; once the instance is ACTIVE and cloud-init completes, the site is live.
Deploy your own content#
Replace the placeholder page with your site files from your local machine:
scp -r ./my-site/* ubuntu@$INSTANCE_IP:/var/www/html/Or use rsync for incremental updates:
rsync -avz --delete ./my-site/ ubuntu@$INSTANCE_IP:/var/www/html/What you learned#
- Security groups for SSH, HTTP, and HTTPS, and why each port is opened
- Instance launch with Ubuntu 22.04, flavor
s1a.micro, thePublicEphemeralpublic network, and the web security group - Public addressing with the public IP
PublicEphemeralassigns at boot - Nginx install, static content under
/var/www/html/, local and remote checks - Optional TLS with a self-signed certificate
Next steps#
- How to point a domain at a Quake AI resource: map a hostname at your registrar to the instance public IP
- How to issue and auto-renew a TLS certificate with Let's Encrypt: production HTTPS with Certbot on the instance
- How to put a CDN in front of a Quake AI workload: edge caching when traffic grows beyond a single VM
- How to host a static site on object storage: serve files from a bucket instead of Nginx on a VM
- Edge reverse proxy template: route traffic to several application instances
- Resource tiers: plan your upgrade path if traffic grows beyond the Developer Plan's 0.5 Gbps cap
Clean up#
Delete the server, then remove the security group. The PublicEphemeral public IP is released with the instance, so there is no floating IP to delete:
openstack server delete tutorial-web-vm
openstack security group delete tutorial-static-webConfirm openstack server list no longer shows tutorial-web-vm.
Usage Guidelines
The sample code, software libraries, command line tools, proofs of concept, templates, and other related technology on this page (including any of the foregoing that is provided by Quake AI personnel) is provided to you as Quake AI Content under the Quake AI Customer Agreement, or the relevant written agreement between you and Quake AI (whichever applies). Do not use this Quake AI Content in your production accounts, or on production or other critical data. You are responsible for testing, securing, and optimizing the Quake AI Content (such as sample code) as appropriate for production grade use based on your specific quality control practices and standards. Deploying Quake AI Content may incur Quake AI charges for creating or using Quake AI chargeable resources, such as running Compute instances or storing data in Object Storage. Your use is also subject to the Acceptable Use Policy.
For the full policy, see Usage Guidelines.
Last validated: 04.06.2026
Quick answers
- Why does `openstack image save` write a 0-byte file for my boot-from-volume instance?CLI
- Why does `openstack server create` fail with "Only volume-backed servers are allowed for flavors with zero disk"?CLIAPITerraform
- Why does my project still have a 10 GiB Cinder volume after I deleted my instance?CLIAPI