The Complete Guide to Deploying Quarto Projects to the Cloud
By Kwizera Jean, Kwiz Computing Technologies • October 20, 2025 • 33 min read
Introduction: Publishing Your Quarto Work to the World
You’ve created beautiful Quarto websites, reports, presentations, or books. Now comes the crucial question: How do I share this with the world?
Quarto is an incredibly powerful open-source scientific and technical publishing system that can create websites, blogs, books, presentations and more. But having a great Quarto project locally is only half the battle—you need to deploy it where others can access it.
This comprehensive guide will walk you through deploying Quarto projects to popular hosting platforms and the three major cloud providers. Whether you’re a beginner looking for the easiest deployment or an enterprise user needing advanced features, this guide has you covered.
- Quick Start Options: Deploy in minutes with Netlify, Vercel, GitHub Pages, Cloudflare Pages, Render, Surge.sh, WordPress hosting, or DigitalOcean
- Enterprise Cloud Platforms: In-depth guides for Google Cloud (GCP), Microsoft Azure and Amazon AWS
- Cost comparisons across all platforms to help you make informed decisions
- Best practices for CI/CD and automated deployments
- Troubleshooting tips for common deployment issues
- Platform selection guide to choose the right hosting for your needs
Understanding Quarto Outputs
Before we dive into cloud platforms, let’s understand what we’re deploying. Quarto can generate several types of outputs:
Static Websites
The most common Quarto output—HTML files, CSS, JavaScript and images that can be served by any web server. This includes:
- Quarto websites (
quarto create website) - Quarto blogs (
quarto create blog) - Quarto books (
quarto create book) - Single documents rendered to HTML
Deployment requirement: Simple static web hosting
Interactive Documents
Documents with interactive elements like Shiny, Observable JS, or Plotly:
- Shiny applications (require server-side R/Python)
- Observable JS (client-side, works as static files)
- Plotly/interactive visualizations (client-side)
Deployment requirement: Static hosting for Observable/Plotly; server hosting for Shiny
Documents (PDF, Word, etc.)
Non-web outputs that you may want to share:
- PDF reports
- Word documents
- PowerPoint presentations
Deployment requirement: File storage or download hosting
This guide primarily focuses on deploying static Quarto websites (the most common use case). We’ll also cover deploying Shiny-enabled Quarto documents, which require server-side computation.
Platform Comparison: Choosing Your Cloud Provider
Each cloud platform has strengths and weaknesses. Here’s a comprehensive comparison to help you decide:
Quick Comparison Table
| Criteria | Google Cloud Platform | Microsoft Azure | Amazon AWS |
|---|---|---|---|
| Monthly Cost (Small Site) | $0-5 | $0-5 | $0-3 |
| Monthly Cost (Medium Site) | $10-30 | $15-40 | $10-25 |
| Free Tier | 1GB free egress/month | Limited (12 months) | 5GB free storage, 15GB transfer |
| Setup Complexity | Low (Firebase) / Medium (Cloud Storage) | Medium | Medium-High |
| Best For | Quick deploys, Firebase users | Enterprise, .NET developers | Scalability, enterprise |
| Custom Domain Cost | Free | Free | Free |
| SSL Certificate | Free (auto) | Free (auto) | Free (ACM) |
| Global CDN | Included | Included | Via CloudFront |
Decision Guide
Choose Google Cloud Platform if: - ✅ You want the easiest setup (especially with Firebase) - ✅ You’re already using Google services (Analytics, etc.) - ✅ You want excellent documentation and tutorials - ✅ You prefer simplicity over advanced features
Choose Microsoft Azure if: - ✅ Your organization uses Microsoft 365 or Azure services - ✅ You need strong enterprise integration - ✅ You work primarily with .NET or Microsoft technologies - ✅ You want excellent technical support
Choose Amazon AWS if: - ✅ You need maximum scalability and control - ✅ You’re building complex architectures - ✅ You want the most comprehensive cloud services - ✅ You’re comfortable with more complex configurations
If you’re new to cloud deployment, we recommend starting with the Quick Start platforms below (Netlify, Vercel, or GitHub Pages). They’re incredibly easy and free for most use cases!
For enterprise needs or advanced features, jump to the major cloud platforms (GCP, Azure, AWS) covered later.
Quick Start: Easiest Deployment Options
Before diving into enterprise cloud platforms, let’s cover the simplest and fastest ways to deploy your Quarto site. These platforms are perfect for personal blogs, documentation sites, portfolios and small business websites.
Why Start Here?
- ⚡ Deploy in minutes, not hours
- 💰 Free tiers that are genuinely generous
- 🔄 Automatic CI/CD from Git commits
- 🌍 Global CDN included by default
- 🔒 Free SSL certificates automatically provisioned
- 🎯 No infrastructure knowledge required
Platform 1: Netlify (Recommended for Beginners)
Netlify is arguably the easiest way to deploy a Quarto site. It’s beloved by developers for its simplicity.
Why Choose Netlify?
- ✅ Zero configuration for static sites
- ✅ 100 GB bandwidth/month free
- ✅ Deploy previews for every pull request
- ✅ Instant rollbacks if something breaks
- ✅ Form handling built-in (useful for contact forms)
Step-by-Step Deployment
Method 1: Drag and Drop (Fastest)
Render your site locally:
quarto renderGo to Netlify Drop
Drag the
_sitefolder onto the pageDone! Your site is live at
https://random-name.netlify.app
Method 2: Git-Based Deployment (Recommended)
Push your Quarto project to GitHub/GitLab
Sign in to Netlify with your Git provider
Click “Add new site” → “Import an existing project”
Select your repository
Configure build settings:
- Build command:
quarto render - Publish directory:
_site - Environment variables (click “Show advanced”):
- Add
PYTHON_VERSION:3.11
- Add
- Build command:
Click “Deploy site”
That’s it! Every push to your main branch automatically deploys.
Custom Domain Setup
- In Netlify dashboard → Domain settings
- Click “Add custom domain”
- Enter your domain (e.g.,
www.yoursite.com) - Add the DNS records shown (usually a CNAME)
- SSL certificate auto-provisions in minutes
Example netlify.toml (Optional but Recommended)
Create this file in your project root for better control:
[build]
command = "quarto render"
publish = "_site"
[build.environment]
PYTHON_VERSION = "3.11"
[context.production.environment]
QUARTO_PRINT_STACK = "true"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
force = false
[[headers]]
for = "/*"
[headers.values]
X-Frame-Options = "DENY"
X-XSS-Protection = "1; mode=block"Cost: Free for 100 GB/month, $19/month for 1 TB.
Platform 2: Vercel (Best Developer Experience)
Vercel offers an exceptional developer experience with blazing-fast deployments.
Why Choose Vercel?
- ✅ Lightning-fast CDN (often faster than Netlify)
- ✅ 100 GB bandwidth/month free
- ✅ Preview deployments for every PR
- ✅ Zero-config for most frameworks
- ✅ Excellent analytics (free tier)
Deployment Steps
Push your project to GitHub
Go to Vercel and sign in
Click “Add New Project”
Import your repository
Vercel auto-detects settings (or configure):
- Framework Preset: Other
- Build Command:
quarto render - Output Directory:
_site
Add environment variable (if using Python):
PYTHON_VERSION:3.11
Deploy!
Custom Domain
- Go to Project Settings → Domains
- Add your domain
- Update DNS as instructed
- Vercel handles SSL automatically
Example vercel.json (Optional)
{
"buildCommand": "quarto render",
"outputDirectory": "_site",
"installCommand": "pip install -r requirements.txt && npm install",
"framework": null,
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "X-Frame-Options",
"value": "SAMEORIGIN"
}
]
}
],
"redirects": [
{
"source": "/:path((?!.*\\.).*)",
"destination": "/:path.html",
"statusCode": 308
}
]
}Cost: Free for personal/hobby projects, $20/month for teams.
Platform 3: GitHub Pages (100% Free Forever)
GitHub Pages is perfect if your code is already on GitHub. Completely free with no bandwidth limits!
Why Choose GitHub Pages?
- ✅ Completely free with unlimited bandwidth
- ✅ No signup needed if you use GitHub
- ✅ Custom domains supported
- ✅ Perfect for open source documentation
- ✅ GitHub Actions for CI/CD
Deployment Steps
Method 1: Using GitHub Actions (Recommended)
- Create
.github/workflows/publish.ymlin your repo:
name: Publish Quarto Site
on:
push:
branches:
- main
permissions:
contents: write
pages: write
id-token: write
jobs:
build-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Quarto
uses: quarto-dev/quarto-actions/setup@v2
with:
version: 1.4.549
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install Python dependencies
run: pip install -r requirements.txt
- name: Render Quarto Site
run: quarto render
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./_site- Enable GitHub Pages:
- Go to repo Settings → Pages
- Source: Deploy from a branch
- Branch:
gh-pages/root - Click Save
- Push to main and watch the action deploy!
Method 2: Using Quarto’s Built-in Command
# Render and publish in one command
quarto publish gh-pages
# Follow the promptsYour site will be live at https://username.github.io/repo-name/
Custom Domain
Add a file called
CNAMEto your project root:www.yoursite.comIn your repo Settings → Pages → Custom domain, enter your domain
Add DNS records at your domain registrar:
- CNAME record:
www→username.github.io - A records for apex domain pointing to GitHub’s IPs
- CNAME record:
Cost: $0 forever!
Platform 4: Render (Modern & Simple)
Render is a modern platform with great simplicity and reliability.
Why Choose Render?
- ✅ Free tier with 100 GB bandwidth
- ✅ Auto-deploys from Git
- ✅ Easy custom domains
- ✅ DDoS protection included
- ✅ Great for full-stack (if you later add APIs)
Deployment Steps
Sign up at Render
Click “New +” → “Static Site”
Connect your Git repository
Configure:
- Name: Your site name
- Build Command:
quarto render - Publish Directory:
_site
Add environment variable (if needed):
PYTHON_VERSION:3.11
Create Static Site
Render auto-deploys on every push to main!
Custom Domain
- Go to Settings → Custom Domain
- Add your domain
- Update DNS as shown
- SSL auto-provisions
Cost: Free tier, $7/month for priority builds.
Platform 5: Surge.sh (Ultra-Simple CLI)
Surge is perfect for quick one-off deployments via CLI.
Why Choose Surge?
- ✅ Instant deployment from command line
- ✅ No account needed upfront
- ✅ Free subdomain (
.surge.sh) - ✅ Custom domains supported
- ✅ Perfect for demos and quick shares
Deployment Steps
Install Surge:
npm install -g surgeRender your Quarto site:
quarto renderDeploy:
cd _site surgeFollow prompts:
- First time: enter email and password
- Choose domain (e.g.,
my-quarto-site.surge.sh)
That’s it! Your site is live in seconds.
Custom Domain
surge _site yourdomain.comThen add a CNAME record in DNS pointing to na-west1.surge.sh
Cost: Free for basic use, $30/month for Pro (SSL + custom features).
Platform 6: Cloudflare Pages (Fast & Free CDN)
Cloudflare Pages leverages Cloudflare’s massive CDN network.
Why Choose Cloudflare Pages?
- ✅ Unlimited bandwidth on free tier
- ✅ 500 builds/month free
- ✅ Fastest CDN globally (Cloudflare’s network)
- ✅ Great analytics built-in
- ✅ Web3/IPFS support if needed
Deployment Steps
Sign in to Cloudflare Pages
Create a project and connect your Git repo
Configure build:
- Build command:
quarto render - Build output directory:
_site - Environment variables:
PYTHON_VERSION=3.11
- Build command:
Save and Deploy
Auto-deploys on every commit!
Cost: Free unlimited, $20/month for additional features.
Platform 7: WordPress (For Existing WordPress Users)
If you already have WordPress hosting, you can deploy your Quarto site as static files.
Why Use WordPress Hosting?
- ✅ You already pay for it
- ✅ Familiar cPanel/FTP interface
- ✅ Can integrate with WordPress blog
- ✅ No extra cost
Deployment via FTP
Render your site:
quarto renderConnect via FTP (use FileZilla or similar):
- Host: Your WordPress site’s FTP address
- Username: Your FTP username
- Password: Your FTP password
Upload
_site/contents to one of these locations:- Subdomain:
/public_html/docs/(accessible atyoursite.com/docs) - Subdomain: Create
docs.yoursite.comin cPanel, upload to its folder - Subfolder:
/public_html/quarto/(accessible atyoursite.com/quarto)
- Subdomain:
Set permissions: Ensure files are readable (644 for files, 755 for directories)
Deployment via cPanel File Manager
- Log in to cPanel
- Go to File Manager
- Navigate to
public_htmlor your subfolder - Click Upload
- Zip your
_site/folder locally and upload - Extract in cPanel
WordPress Subdirectory Setup
Create a subdirectory /quarto/ in your WordPress installation and upload there. Your Quarto site will be available at yoursite.com/quarto/
Add to .htaccess for cleaner URLs:
# In /public_html/quarto/.htaccess
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ $1.html [L]Pros: No extra cost, simple for WordPress users Cons: Manual upload process (no auto-deploy from Git)
Platform 8: DigitalOcean App Platform
DigitalOcean App Platform offers simple deployment with great documentation.
Why Choose DigitalOcean?
- ✅ $0/month for static sites (3 free sites)
- ✅ 1 GB bandwidth/month free
- ✅ Great docs and tutorials
- ✅ Easy to scale to full apps later
- ✅ Integrated with DO ecosystem
Deployment Steps
Sign in to DigitalOcean
Go to Apps → Create App
Connect your GitHub/GitLab repo
Configure build:
- Type: Static Site
- Build Command:
quarto render - Output Directory:
_site
Select free plan (Static Site - $0/mo)
Launch App
Cost: Free for static sites (up to 3 sites), $5/month for additional.
Quick Comparison: Which Platform to Choose?
| Platform | Ease of Setup | Free Tier | Auto Deploy | Custom Domain | Best For |
|---|---|---|---|---|---|
| Netlify | ★★★★★ | 100 GB/mo | Yes | Free | Beginners |
| Vercel | ★★★★★ | 100 GB/mo | Yes | Free | Developers |
| GitHub Pages | ★★★★☆ | Unlimited | Yes | Free | Open Source |
| Render | ★★★★☆ | 100 GB/mo | Yes | Free | Simple projects |
| Surge.sh | ★★★★★ | Basic | CLI | Free | Quick demos |
| Cloudflare Pages | ★★★★☆ | Unlimited | Yes | Free | Performance |
| WordPress Hosting | ★★★☆☆ | N/A (paid) | Manual | Included | WP users |
| DigitalOcean | ★★★★☆ | 1 GB/mo | Yes | Free | DO ecosystem |
Our Recommendations
For absolute beginners: Start with Netlify (drag-and-drop deployment)
For developers: Use Vercel or Netlify (both excellent)
For open source projects: Use GitHub Pages (free forever)
For quick demos: Use Surge.sh (instant CLI deployment)
For WordPress users: Upload to existing WordPress hosting
For maximum performance: Use Cloudflare Pages (fastest CDN)
For DigitalOcean users: Use DO App Platform (free tier)
Part 1: Deploying to Google Cloud Platform
Google Cloud offers two main options for hosting static sites: Firebase Hosting (easiest) and Cloud Storage (more control). We’ll cover both.
Option A: Firebase Hosting (Recommended)
Firebase Hosting is Google’s streamlined hosting solution, perfect for Quarto sites.
Prerequisites
- A Google account
- Node.js and npm installed
- Your Quarto project rendered locally
Step 1: Install Firebase CLI
Open your terminal and install the Firebase CLI globally:
npm install -g firebase-toolsVerify installation:
firebase --versionStep 2: Login to Firebase
Authenticate with your Google account:
firebase loginThis will open your browser for authentication. Sign in with your Google account.
Step 3: Initialize Firebase in Your Project
Navigate to your Quarto project directory:
cd /path/to/your/quarto-projectInitialize Firebase:
firebase init hostingYou’ll be prompted with several questions:
- “Please select an option”: Choose “Create a new project” or select an existing one
- “What do you want to use as your public directory?”: Enter
_site(Quarto’s default output directory) - “Configure as a single-page app?”: Choose
No(unless you have specific SPA requirements) - “Set up automatic builds and deploys with GitHub?”: Choose
Yesif you want CI/CD,Nofor manual deploys
Step 4: Render Your Quarto Project
Before deploying, render your Quarto site:
quarto renderThis creates the _site directory with your built website.
Step 5: Deploy to Firebase
Deploy your site:
firebase deploy --only hostingYou’ll see output like:
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/your-project/overview
Hosting URL: https://your-project.web.app
🎉 Your site is live! Visit the Hosting URL to see it.
Step 6: Custom Domain (Optional)
To use a custom domain like www.yoursite.com:
- Go to Firebase Console
- Select your project → Hosting
- Click “Add custom domain”
- Follow the instructions to verify ownership (add DNS records)
Whenever you make changes:
- Edit your Quarto files
- Run
quarto render - Run
firebase deploy --only hosting
Your site updates in seconds!
Option B: Google Cloud Storage + Cloud CDN
For more control, use Cloud Storage with a load balancer and CDN.
Step 1: Install and Configure gcloud CLI
Download and install the Google Cloud SDK:
# For macOS/Linux (using script)
curl https://sdk.cloud.google.com | bash
# Initialize gcloud
gcloud initStep 2: Create a Cloud Storage Bucket
Create a bucket with your domain name (or any unique name):
# Replace with your domain or project name
export BUCKET_NAME="my-quarto-site.com"
export PROJECT_ID="your-project-id"
# Create bucket
gcloud storage buckets create gs://$BUCKET_NAME \
--project=$PROJECT_ID \
--location=US \
--uniform-bucket-level-accessStep 3: Configure Bucket for Web Hosting
Make the bucket public and set index/error pages:
# Make bucket publicly readable
gcloud storage buckets add-iam-policy-binding gs://$BUCKET_NAME \
--member=allUsers \
--role=roles/storage.objectViewer
# Set main page and error page
gcloud storage buckets update gs://$BUCKET_NAME \
--web-main-page-suffix=index.html \
--web-error-page=404.htmlStep 4: Upload Your Quarto Site
Render and upload:
# Render your site
quarto render
# Upload to Cloud Storage
gcloud storage rsync _site/ gs://$BUCKET_NAME --recursive --delete-unmatched-destination-objectsStep 5: Set Up Cloud CDN (Optional but Recommended)
For global performance, set up a load balancer with Cloud CDN:
# Create backend bucket
gcloud compute backend-buckets create quarto-backend \
--gcs-bucket-name=$BUCKET_NAME \
--enable-cdn
# Create URL map
gcloud compute url-maps create quarto-url-map \
--default-backend-bucket=quarto-backend
# Create SSL certificate (for HTTPS)
gcloud compute ssl-certificates create quarto-ssl-cert \
--domains=$BUCKET_NAME
# Create HTTPS proxy
gcloud compute target-https-proxies create quarto-https-proxy \
--url-map=quarto-url-map \
--ssl-certificates=quarto-ssl-cert
# Create forwarding rule
gcloud compute forwarding-rules create quarto-https-rule \
--global \
--target-https-proxy=quarto-https-proxy \
--ports=443Your site is now served globally with CDN acceleration! 🚀
Cloud Storage charges for: - Storage: ~$0.020 per GB/month - Network egress: ~$0.12 per GB (after free tier) - Operations: Minimal costs for reads/writes
A typical small Quarto site (100MB, 10,000 visits/month) costs $1-3/month.
Part 2: Deploying to Microsoft Azure
Azure offers Azure Static Web Apps (easiest) and Azure Storage (traditional). We’ll cover both.
Option A: Azure Static Web Apps (Recommended)
Azure Static Web Apps is purpose-built for static sites with built-in CI/CD.
Prerequisites
- An Azure account (free tier available)
- Azure CLI installed
- Your Quarto project in a GitHub repository (for CI/CD)
Step 1: Install Azure CLI
# macOS
brew install azure-cli
# Windows (using winget)
winget install Microsoft.AzureCLI
# Linux
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bashVerify installation:
az --versionStep 2: Login to Azure
az loginThis opens your browser for authentication.
Step 3: Create a Static Web App via Azure Portal
- Go to Azure Portal
- Click “Create a resource” → Search for “Static Web Apps”
- Click “Create”
Configure your app:
- Subscription: Choose your subscription
- Resource Group: Create new or select existing
- Name: Your app name (e.g.,
my-quarto-site) - Plan type: Free (for small sites)
- Region: Choose closest to your audience
- Source: Select “GitHub” and authorize
- Organization: Your GitHub username/org
- Repository: Your Quarto project repo
- Branch:
mainor your deployment branch
Build configuration:
- Build Presets: Select “Custom”
- App location:
/(root of repo) - Api location: Leave empty (unless using Azure Functions)
- Output location:
_site
Click “Review + create” → “Create”
Step 4: Configure GitHub Actions Workflow
Azure automatically creates a GitHub Actions workflow. Update it for Quarto:
In your repo, edit .github/workflows/azure-static-web-apps-xxx.yml:
name: Azure Static Web Apps CI/CD
on:
push:
branches:
- main
pull_request:
types: [opened, synchronize, reopened, closed]
branches:
- main
jobs:
build_and_deploy_job:
if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
runs-on: ubuntu-latest
name: Build and Deploy Job
steps:
- uses: actions/checkout@v3
with:
submodules: true
# Install Quarto
- name: Set up Quarto
uses: quarto-dev/quarto-actions/setup@v2
with:
version: 1.4.549 # or latest
# Install R (if using R)
- name: Set up R
uses: r-lib/actions/setup-r@v2
with:
r-version: '4.3.0'
# Install Python (if using Python)
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
# Install dependencies
- name: Install R dependencies
run: |
Rscript -e 'install.packages(c("rmarkdown", "knitr"))'
# Render Quarto project
- name: Render Quarto Project
run: quarto render
# Deploy to Azure
- name: Deploy to Azure Static Web Apps
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_XXX }}
repo_token: ${{ secrets.GITHUB_TOKEN }}
action: "upload"
app_location: "_site"
skip_app_build: true
close_pull_request_job:
if: github.event_name == 'pull_request' && github.event.action == 'closed'
runs-on: ubuntu-latest
name: Close Pull Request Job
steps:
- name: Close Pull Request
uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_XXX }}
action: "close"Commit and push this file. Every push to main will now automatically deploy!
Step 5: Access Your Site
Your site is available at: https://<app-name>.azurestaticapps.net
Find your URL in Azure Portal → Your Static Web App → Overview
Step 6: Custom Domain (Optional)
- In Azure Portal, go to your Static Web App
- Click “Custom domains” in the left menu
- Click “Add”
- Enter your domain (e.g.,
www.yoursite.com) - Add the provided CNAME record to your DNS provider
- Wait for DNS propagation (can take up to 48 hours)
Azure automatically provisions and manages SSL certificates! 🔒
Option B: Azure Blob Storage
For a traditional static hosting approach:
Step 1: Create a Storage Account
# Set variables
RESOURCE_GROUP="quarto-rg"
LOCATION="eastus"
STORAGE_ACCOUNT="quartositestorage" # must be globally unique
# Create resource group
az group create --name $RESOURCE_GROUP --location $LOCATION
# Create storage account
az storage account create \
--name $STORAGE_ACCOUNT \
--resource-group $RESOURCE_GROUP \
--location $LOCATION \
--sku Standard_LRS \
--kind StorageV2Step 2: Enable Static Website Hosting
az storage blob service-properties update \
--account-name $STORAGE_ACCOUNT \
--static-website \
--index-document index.html \
--404-document 404.htmlStep 3: Upload Your Quarto Site
# Render site
quarto render
# Upload files
az storage blob upload-batch \
--account-name $STORAGE_ACCOUNT \
--source _site \
--destination '$web' \
--overwriteStep 4: Get Your Website URL
az storage account show \
--name $STORAGE_ACCOUNT \
--resource-group $RESOURCE_GROUP \
--query "primaryEndpoints.web" \
--output tsvYour site is live at the URL provided! 🎉
Azure Static Web Apps Free tier includes: - 100 GB bandwidth/month - 2 custom domains - Free SSL certificates
Perfect for most Quarto sites!
Part 3: Deploying to Amazon AWS
AWS offers several options: S3 + CloudFront (most popular) and AWS Amplify (easiest). We’ll cover both.
Option A: AWS Amplify (Recommended for Beginners)
AWS Amplify provides streamlined hosting with CI/CD integration.
Prerequisites
- An AWS account
- Your Quarto project in a GitHub repository
- AWS CLI installed (optional)
Step 1: Deploy via AWS Console
- Go to AWS Amplify Console
- Click “Get Started” under “Amplify Hosting”
- Select “GitHub” (or GitLab, Bitbucket)
- Authorize AWS Amplify with your GitHub account
- Select your repository and branch
Step 2: Configure Build Settings
Amplify will try to detect your build settings. Update the amplify.yml:
version: 1
frontend:
phases:
preBuild:
commands:
# Install Quarto
- wget https://github.com/quarto-dev/quarto-cli/releases/download/v1.4.549/quarto-1.4.549-linux-amd64.deb
- dpkg -i quarto-1.4.549-linux-amd64.deb
# Install R (if needed)
- apt-get update
- apt-get install -y r-base
# Install R packages (if needed)
- Rscript -e 'install.packages(c("rmarkdown", "knitr", "ggplot2"), repos="https://cran.rstudio.com/")'
build:
commands:
- quarto render
artifacts:
baseDirectory: _site
files:
- '**/*'
cache:
paths: []Save this as amplify.yml in your repository root.
Step 3: Deploy
Click “Save and Deploy”. Amplify will:
- Clone your repository
- Install Quarto and dependencies
- Render your site
- Deploy to CloudFront CDN
You’ll get a URL like: https://main.d1234abcd.amplifyapp.com
Step 4: Custom Domain (Optional)
- In Amplify Console, select your app
- Click “Domain management” → “Add domain”
- Enter your domain
- Amplify provides DNS records to add
- Add records to your DNS provider
- SSL certificate is automatically provisioned
Every push to your repository automatically triggers a new build and deployment. Check build status in the Amplify Console.
Option B: S3 + CloudFront (Production Setup)
For maximum control and cost optimization:
Step 1: Install AWS CLI
# macOS
brew install awscli
# Windows
# Download installer from https://aws.amazon.com/cli/
# Linux
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/installConfigure AWS CLI:
aws configureEnter your AWS Access Key ID, Secret Access Key, region and output format.
Step 2: Create an S3 Bucket
# Set variables
BUCKET_NAME="my-quarto-site.com" # Use your domain
REGION="us-east-1"
# Create bucket
aws s3 mb s3://$BUCKET_NAME --region $REGION
# Configure for website hosting
aws s3 website s3://$BUCKET_NAME \
--index-document index.html \
--error-document 404.htmlStep 3: Create Bucket Policy for Public Access
Create a file bucket-policy.json:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-quarto-site.com/*"
}
]
}Apply the policy:
aws s3api put-bucket-policy \
--bucket $BUCKET_NAME \
--policy file://bucket-policy.jsonStep 4: Upload Your Quarto Site
# Render site
quarto render
# Upload to S3 (sync keeps it up-to-date)
aws s3 sync _site/ s3://$BUCKET_NAME --deleteYour site is now available at: http://my-quarto-site.com.s3-website-us-east-1.amazonaws.com
Step 5: Set Up CloudFront CDN (Recommended)
For HTTPS and global performance:
- Create CloudFront Distribution:
aws cloudfront create-distribution \
--origin-domain-name $BUCKET_NAME.s3-website-$REGION.amazonaws.com \
--default-root-object index.html- Or use AWS Console:
- Go to CloudFront Console
- Click “Create Distribution”
- Origin Domain: Select your S3 bucket website endpoint
- Viewer Protocol Policy: “Redirect HTTP to HTTPS”
- Default Root Object:
index.html - Click “Create Distribution”
Wait 10-20 minutes for deployment.
Step 6: Request SSL Certificate (for Custom Domain)
# Request certificate (must be in us-east-1 for CloudFront)
aws acm request-certificate \
--domain-name yourdomain.com \
--domain-name www.yourdomain.com \
--validation-method DNS \
--region us-east-1Verify domain ownership by adding DNS records shown in ACM Console.
Step 7: Configure Custom Domain
- In CloudFront distribution settings:
- Alternate Domain Names (CNAMEs): Add
yourdomain.comandwww.yourdomain.com - SSL Certificate: Select your ACM certificate
- Alternate Domain Names (CNAMEs): Add
- In your DNS provider, create a CNAME record:
- Name:
wwwor@ - Value: Your CloudFront distribution domain (e.g.,
d123abc.cloudfront.net)
- Name:
Your site is now live with HTTPS! 🔒
After updating your site, invalidate CloudFront cache:
# Get distribution ID
DISTRIBUTION_ID=$(aws cloudfront list-distributions \
--query "DistributionList.Items[?Origins.Items[?DomainName=='$BUCKET_NAME.s3-website-$REGION.amazonaws.com']].Id" \
--output text)
# Create invalidation
aws cloudfront create-invalidation \
--distribution-id $DISTRIBUTION_ID \
--paths "/*"This ensures visitors see the latest version.
AWS Costs Breakdown
| Service | Monthly Cost | Notes |
|---|---|---|
| S3 Storage (1 GB) | $0.023 | First 5 GB free (12 months) |
| S3 Requests (10k/mo) | $0.005 | Minimal cost |
| CloudFront Data Transfer (10 GB) | $0.85 | First 1 TB: $0.085/GB |
| CloudFront Requests (100k) | $0.01 | First 10M: $0.0075/10k |
| Route 53 Hosted Zone | $0.50 | Per hosted zone |
| Total (Small Site) | $1.38 | With free tier |
A typical small Quarto site costs $1-3/month on AWS.
Part 4: Deploying Shiny-Enabled Quarto Documents
If your Quarto document includes Shiny interactive elements, you need server-side hosting.
Prerequisites
Your Quarto document with Shiny runtime:
---
title: "My Interactive Document"
format: html
server: shiny
---Option 1: Deploy to Posit Connect (Easiest)
Posit Connect (formerly RStudio Connect) is purpose-built for R/Python content.
# Install rsconnect
install.packages("rsconnect")
# Configure your account (get credentials from Posit Connect)
rsconnect::setAccountInfo(
name = "your-server",
url = "https://connect.yourcompany.com",
token = "YOUR_TOKEN",
secret = "YOUR_SECRET"
)
# Deploy
rsconnect::deployDoc("your-document.qmd")Option 2: Deploy to ShinyApps.io
ShinyApps.io is Posit’s hosted Shiny service.
# Install rsconnect
install.packages("rsconnect")
# Configure account
rsconnect::setAccountInfo(
name = "your-account",
token = "YOUR_TOKEN",
secret = "YOUR_SECRET"
)
# Deploy
rsconnect::deployDoc("your-document.qmd")Pricing: Free tier allows 5 apps, 25 active hours/month.
Option 3: Self-Host on Cloud VMs
For full control, deploy to a cloud VM running Shiny Server.
AWS EC2 Example
# Launch EC2 instance (Ubuntu 22.04, t2.micro)
# Install R and Shiny Server
sudo apt-get update
sudo apt-get install -y r-base gdebi-core
# Download and install Shiny Server
wget https://download3.rstudio.org/ubuntu-18.04/x86_64/shiny-server-1.5.20.1002-amd64.deb
sudo gdebi shiny-server-1.5.20.1002-amd64.deb
# Install Quarto
wget https://github.com/quarto-dev/quarto-cli/releases/download/v1.4.549/quarto-1.4.549-linux-amd64.deb
sudo dpkg -i quarto-1.4.549-linux-amd64.deb
# Copy your Quarto doc to Shiny Server directory
sudo cp your-document.qmd /srv/shiny-server/
# Restart Shiny Server
sudo systemctl restart shiny-serverAccess your app at: http://your-ec2-ip:3838/your-document.qmd
- Use NGINX as a reverse proxy for HTTPS
- Set up automatic backups of your content
- Monitor resource usage with CloudWatch/Azure Monitor/Google Cloud Monitoring
- Consider autoscaling for high-traffic apps
Part 5: CI/CD and Automation
Automating your deployment ensures your site stays up-to-date with minimal effort.
GitHub Actions for Multi-Platform Deployment
Here’s a comprehensive GitHub Actions workflow that renders your Quarto site and deploys to your chosen platform:
.github/workflows/deploy-quarto.yml:
name: Deploy Quarto Site
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Quarto
uses: quarto-dev/quarto-actions/setup@v2
with:
version: 1.4.549
- name: Set up R
uses: r-lib/actions/setup-r@v2
with:
r-version: '4.3.0'
- name: Install R dependencies
run: |
Rscript -e 'install.packages(c("rmarkdown", "knitr", "ggplot2", "dplyr"))'
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install Python dependencies
run: |
pip install jupyter matplotlib pandas plotly
- name: Render Quarto Project
run: quarto render
# Deploy to Firebase
- name: Deploy to Firebase
if: github.ref == 'refs/heads/main'
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT }}'
channelId: live
projectId: your-firebase-project
# OR Deploy to AWS S3 + CloudFront
# - name: Configure AWS credentials
# if: github.ref == 'refs/heads/main'
# uses: aws-actions/configure-aws-credentials@v2
# with:
# aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
# aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# aws-region: us-east-1
#
# - name: Deploy to S3
# if: github.ref == 'refs/heads/main'
# run: |
# aws s3 sync _site/ s3://your-bucket-name --delete
# aws cloudfront create-invalidation --distribution-id YOUR_DIST_ID --paths "/*"
# OR Deploy to Azure
# - name: Deploy to Azure Static Web Apps
# if: github.ref == 'refs/heads/main'
# uses: Azure/static-web-apps-deploy@v1
# with:
# azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
# repo_token: ${{ secrets.GITHUB_TOKEN }}
# action: "upload"
# app_location: "_site"
# skip_app_build: trueSetting Up Secrets
For each platform, add secrets to your GitHub repository:
- Go to your repo → Settings → Secrets and variables → Actions
- Click “New repository secret”
For Firebase: - FIREBASE_SERVICE_ACCOUNT: Generate in Firebase Console → Project Settings → Service Accounts
For AWS: - AWS_ACCESS_KEY_ID: From IAM user - AWS_SECRET_ACCESS_KEY: From IAM user
For Azure: - AZURE_STATIC_WEB_APPS_API_TOKEN: Automatically created when you set up Static Web Apps
- Test before deploying: Add a test job that renders the site and checks for errors
- Deploy on branch merge: Only deploy from
mainbranch - Use environment variables: Store sensitive data in GitHub Secrets
- Monitor builds: Set up notifications for failed deployments
- Implement staging: Deploy PRs to preview URLs for testing
Part 6: Performance Optimization
Once deployed, optimize your site for speed and user experience.
1. Image Optimization
Large images slow down your site. Optimize them:
# Install tools
brew install imagemagick jpegoptim pngquant
# Optimize JPEGs
jpegoptim --max=85 --strip-all images/*.jpg
# Optimize PNGs
pngquant --quality=65-80 images/*.pngOr use Quarto’s built-in optimization in _quarto.yml:
format:
html:
fig-width: 8
fig-height: 6
fig-format: retina2. Enable Compression
Ensure your web server serves compressed files:
For CloudFront (AWS): - In CloudFront distribution settings → Behaviors - Edit behavior → Compress Objects Automatically: Yes
For Firebase: Automatically enabled ✓
For Azure Static Web Apps: Automatically enabled ✓
3. Minimize Asset Size
Remove unused CSS/JS and minify code:
# _quarto.yml
format:
html:
minimal: true
theme: cosmo4. Implement Caching
Set cache headers for static assets:
For S3:
# Cache images for 1 year
aws s3 sync _site/images/ s3://$BUCKET_NAME/images/ \
--cache-control "max-age=31536000,public"
# Cache HTML for 1 hour
aws s3 sync _site/ s3://$BUCKET_NAME/ \
--exclude "images/*" \
--cache-control "max-age=3600,public"5. Use a CDN
All three platforms support CDN:
- GCP: Cloud CDN (automatic with Firebase)
- Azure: Azure CDN (built into Static Web Apps)
- AWS: CloudFront (covered in deployment section)
Test your site’s performance:
Aim for: - Load time < 3 seconds - PageSpeed score > 90 - First Contentful Paint < 1.5s
Part 7: Monitoring and Analytics
Track your site’s performance and visitor behavior.
Google Analytics Integration
Add to your _quarto.yml:
website:
google-analytics: "G-XXXXXXXXXX"Replace with your Google Analytics 4 measurement ID.
Platform-Specific Monitoring
Google Cloud Platform: - Use Cloud Monitoring - Track uptime, latency and errors
Microsoft Azure: - Use Application Insights - Track page views, performance and exceptions
Amazon AWS: - Use CloudWatch - Monitor S3 bucket metrics and CloudFront analytics
Uptime Monitoring
Free tools to monitor site availability:
- UptimeRobot - Free for 50 monitors
- Pingdom - Free trial
- StatusCake - Free plan available
Part 8: Troubleshooting Common Issues
Issue 1: 404 Errors on Page Refresh (SPAs)
Problem: Refreshing a page shows 404 error.
Solution:
For Firebase:
// firebase.json
{
"hosting": {
"public": "_site",
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}For Azure Static Web Apps:
// staticwebapp.config.json
{
"navigationFallback": {
"rewrite": "/index.html"
}
}For AWS CloudFront: - Create a Custom Error Response for 404 → Return index.html with 200 status
Issue 2: Slow Build Times in CI/CD
Problem: GitHub Actions takes >10 minutes to build.
Solutions:
- Cache dependencies:
- name: Cache R packages
uses: actions/cache@v3
with:
path: ${{ env.R_LIBS_USER }}
key: ${{ runner.os }}-r-${{ hashFiles('DESCRIPTION') }}
- name: Cache Quarto
uses: actions/cache@v3
with:
path: ~/.quarto
key: ${{ runner.os }}-quarto-${{ hashFiles('_quarto.yml') }}- Use
freeze: autoin_quarto.yml:
execute:
freeze: auto # Only re-run changed codeIssue 3: Large Files Exceeding Limits
Problem: Deployment fails due to file size limits.
Solutions:
- Use Git LFS for large files:
git lfs install
git lfs track "*.pdf"
git lfs track "*.mp4"- Store large files externally:
- Upload videos to YouTube/Vimeo
- Use cloud storage for large datasets
- Reference via URLs in your Quarto documents
Issue 4: Environment Variables Not Working
Problem: API keys or secrets not accessible.
Solution:
Add environment variables to your build:
GitHub Actions:
- name: Render Quarto Project
env:
API_KEY: ${{ secrets.API_KEY }}
run: quarto renderIn Quarto document:
api_key <- Sys.getenv("API_KEY")Issue 5: CORS Errors with APIs
Problem: Browser blocks API requests from your deployed site.
Solution:
Configure CORS headers:
For Firebase:
// firebase.json
{
"hosting": {
"headers": [
{
"source": "**",
"headers": [
{
"key": "Access-Control-Allow-Origin",
"value": "*"
}
]
}
]
}
}For S3 + CloudFront:
Add CORS configuration to S3 bucket:
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedOrigins": ["*"],
"ExposeHeaders": []
}
]Part 9: Cost Optimization Strategies
Multi-Cloud Cost Comparison
Cost-Saving Tips
- Use Free Tiers Wisely
- GCP Firebase: 1GB storage, 10GB transfer/month free
- Azure Static Web Apps: 100GB bandwidth/month free
- AWS: 5GB S3, 1TB CloudFront transfer (12 months)
- Optimize Images and Assets
- Compress images to reduce storage and bandwidth
- Use WebP format (smaller than JPEG/PNG)
- Lazy-load images with
loading="lazy"
- Implement Aggressive Caching
- Cache static assets for long periods (1 year)
- Use CDN to reduce origin requests
- Clean Up Old Versions
- Delete old build artifacts
- Remove unused files from storage
- Monitor and Alert
- Set up billing alerts in all platforms
- Review monthly costs and optimize
AWS:
aws budgets create-budget \
--account-id YOUR_ACCOUNT_ID \
--budget file://budget.json \
--notifications-with-subscribers file://notifications.jsonGCP:
gcloud billing budgets create \
--billing-account=BILLING_ACCOUNT_ID \
--display-name="Quarto Site Budget" \
--budget-amount=10USDAzure: Set up in Azure Portal → Cost Management → Budgets
Part 10: Advanced Topics
Custom Build Processes
For complex projects, create custom build scripts:
build.sh:
#!/bin/bash
set -e
echo "Installing dependencies..."
Rscript -e 'renv::restore()'
echo "Pre-processing data..."
Rscript scripts/preprocess.R
echo "Rendering Quarto site..."
quarto render
echo "Post-processing..."
python scripts/optimize_images.py _site/
echo "Build complete!"Make executable and use in CI/CD:
chmod +x build.sh
./build.shMulti-Language Projects
Deploy sites in multiple languages:
# _quarto.yml
website:
title: "My Site"
project:
output-dir: _site
format:
html:
toc: true
# Create language-specific folders
# /en/ for English
# /fr/ for French
# /es/ for SpanishUse Firebase redirects or CloudFront functions to detect user language.
A/B Testing
Implement A/B testing with CloudFront Functions:
// CloudFront Function for A/B testing
function handler(event) {
var request = event.request;
var uri = request.uri;
// 50% traffic split
var bucket = Math.random() < 0.5 ? 'a' : 'b';
if (uri === '/') {
request.uri = '/index-' + bucket + '.html';
}
return request;
}Progressive Web Apps (PWA)
Turn your Quarto site into a PWA:
- Create
manifest.json:
{
"name": "My Quarto Site",
"short_name": "QuartoSite",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0066cc",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}- Create
service-worker.js:
const CACHE_NAME = 'quarto-site-v1';
const urlsToCache = [
'/',
'/styles.css',
'/index.html'
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => response || fetch(event.request))
);
});- Reference in your HTML:
<link rel="manifest" href="/manifest.json">
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js');
}
</script>Conclusion: Your Deployment Journey
Congratulations! You now have comprehensive knowledge of deploying Quarto projects to the cloud. Let’s recap:
Key Takeaways
- Platform Selection Matters
- Firebase/GCP: Easiest for beginners
- Azure: Best for Microsoft ecosystem
- AWS: Maximum flexibility and scale
- Automation is Essential
- Set up CI/CD early
- Use GitHub Actions or platform-specific tools
- Test before deploying
- Optimize from Day One
- Compress images
- Enable CDN
- Implement caching
- Monitor performance
- Security First
- Always use HTTPS
- Keep secrets in environment variables
- Regularly update dependencies
- Set up billing alerts
- Monitor and Iterate
- Track analytics
- Monitor uptime
- Review costs monthly
- Optimize based on data
Next Steps
- Choose your platform based on your needs and existing infrastructure
- Follow the step-by-step guide for your chosen platform
- Set up CI/CD to automate future deployments
- Optimize for performance and cost
- Monitor and iterate based on real-world usage
Resources
Quarto Documentation: - Quarto Publishing Guide - Quarto Websites - Quarto Books
Cloud Platform Docs: - Firebase Hosting - Azure Static Web Apps - AWS Amplify - AWS S3 Static Hosting
Community: - Quarto GitHub Discussions - RStudio Community - Stack Overflow - Quarto Tag
If you’re deploying a complex Quarto project and need expert assistance, Kwiz Computing Technologies specializes in:
- Custom Quarto Deployments: Complex multi-site projects, authenticated content, custom domains
- CI/CD Pipeline Setup: Automated testing, preview environments, staging/production workflows
- Performance Optimization: Page speed optimization, CDN configuration, cost reduction
- Enterprise Quarto Solutions: Scalable architectures, high-traffic sites, custom integrations
Contact us for a consultation.
About Kwiz Computing Technologies
We specialize in helping organizations leverage R for production software development, particularly in data-intensive domains. Our expertise includes enterprise-grade Shiny application development using the Rhino framework, R package development and analytical systems that go from prototype to production seamlessly.
Interested in exploring whether R makes sense for your organization? Contact us for a no-obligation consultation where we can discuss your specific challenges and whether R might be part of the solution.
Quick Reference: Deployment Commands
Google Cloud (Firebase)
# Setup
npm install -g firebase-tools
firebase login
firebase init hosting
# Deploy
quarto render
firebase deploy --only hostingAzure (Static Web Apps)
# Setup
az login
# Use Azure Portal to create Static Web App
# Deploy (automatic via GitHub Actions)
git push origin mainAWS (S3 + CloudFront)
# Setup
aws configure
aws s3 mb s3://your-bucket-name
aws s3 website s3://your-bucket-name --index-document index.html
# Deploy
quarto render
aws s3 sync _site/ s3://your-bucket-name --delete
# Invalidate cache
aws cloudfront create-invalidation --distribution-id DIST_ID --paths "/*"Happy deploying! 🚀
Your Quarto projects deserve to be shared with the world. Now you have the knowledge to make that happen—efficiently, securely and cost-effectively.
This guide was created by Kwiz Computing Technologies, specialists in R programming, Quarto publishing and cloud deployment solutions.
Last updated: November 9, 2025
