The Complete Guide to Deploying Quarto Projects to the Cloud

Quarto
Cloud Deployment
Netlify
Vercel
GitHub Pages
AWS
Azure
Google Cloud
WordPress
DevOps
Tutorial
An interactive, comprehensive step-by-step guide to deploying Quarto websites on popular platforms: Netlify, Vercel, GitHub Pages, WordPress, Cloudflare Pages and major cloud providers (GCP, Azure, AWS). Choose your platform and deploy with confidence.
Author

Kwizera Jean

Published

October 20, 2025

By Kwizera Jean, Kwiz Computing Technologies • October 20, 2025 • 33 min read

Introduction: Publishing Your Quarto Work to the World

Cloud deployment concept. Photo by NASA on Unsplash

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.

What You’ll Learn
  • 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

Focus of This Guide

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

First Time Deploying to Cloud?

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 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

  1. Push your project to GitHub

  2. Go to Vercel and sign in

  3. Click “Add New Project”

  4. Import your repository

  5. Vercel auto-detects settings (or configure):

    • Framework Preset: Other
    • Build Command: quarto render
    • Output Directory: _site
  6. Add environment variable (if using Python):

    • PYTHON_VERSION: 3.11
  7. Deploy!

Custom Domain

  1. Go to Project Settings → Domains
  2. Add your domain
  3. Update DNS as instructed
  4. 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)

  1. Create .github/workflows/publish.yml in 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
  1. Enable GitHub Pages:
    • Go to repo Settings → Pages
    • Source: Deploy from a branch
    • Branch: gh-pages / root
    • Click Save
  2. 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 prompts

Your site will be live at https://username.github.io/repo-name/

Custom Domain

  1. Add a file called CNAME to your project root:

    www.yoursite.com
  2. In your repo Settings → Pages → Custom domain, enter your domain

  3. Add DNS records at your domain registrar:

    • CNAME record: wwwusername.github.io
    • A records for apex domain pointing to GitHub’s IPs

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

  1. Sign up at Render

  2. Click “New +” → “Static Site”

  3. Connect your Git repository

  4. Configure:

    • Name: Your site name
    • Build Command: quarto render
    • Publish Directory: _site
  5. Add environment variable (if needed):

    • PYTHON_VERSION: 3.11
  6. Create Static Site

Render auto-deploys on every push to main!

Custom Domain

  1. Go to Settings → Custom Domain
  2. Add your domain
  3. Update DNS as shown
  4. 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

  1. Install Surge:

    npm install -g surge
  2. Render your Quarto site:

    quarto render
  3. Deploy:

    cd _site
    surge
  4. Follow 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.com

Then 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

  1. Sign in to Cloudflare Pages

  2. Create a project and connect your Git repo

  3. Configure build:

    • Build command: quarto render
    • Build output directory: _site
    • Environment variables: PYTHON_VERSION=3.11
  4. 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

  1. Render your site:

    quarto render
  2. Connect via FTP (use FileZilla or similar):

    • Host: Your WordPress site’s FTP address
    • Username: Your FTP username
    • Password: Your FTP password
  3. Upload _site/ contents to one of these locations:

    • Subdomain: /public_html/docs/ (accessible at yoursite.com/docs)
    • Subdomain: Create docs.yoursite.com in cPanel, upload to its folder
    • Subfolder: /public_html/quarto/ (accessible at yoursite.com/quarto)
  4. Set permissions: Ensure files are readable (644 for files, 755 for directories)

Deployment via cPanel File Manager

  1. Log in to cPanel
  2. Go to File Manager
  3. Navigate to public_html or your subfolder
  4. Click Upload
  5. Zip your _site/ folder locally and upload
  6. 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

  1. Sign in to DigitalOcean

  2. Go to Apps → Create App

  3. Connect your GitHub/GitLab repo

  4. Configure build:

    • Type: Static Site
    • Build Command: quarto render
    • Output Directory: _site
  5. Select free plan (Static Site - $0/mo)

  6. 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 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 init

Step 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-access

Step 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.html

Step 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-objects

Part 2: Deploying to Microsoft Azure

Azure offers Azure Static Web Apps (easiest) and Azure Storage (traditional). We’ll cover both.

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 StorageV2

Step 2: Enable Static Website Hosting

az storage blob service-properties update \
    --account-name $STORAGE_ACCOUNT \
    --static-website \
    --index-document index.html \
    --404-document 404.html

Step 3: Upload Your Quarto Site

# Render site
quarto render

# Upload files
az storage blob upload-batch \
    --account-name $STORAGE_ACCOUNT \
    --source _site \
    --destination '$web' \
    --overwrite

Step 4: Get Your Website URL

az storage account show \
    --name $STORAGE_ACCOUNT \
    --resource-group $RESOURCE_GROUP \
    --query "primaryEndpoints.web" \
    --output tsv

Your site is live at the URL provided! 🎉

Azure Costs

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 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/install

Configure AWS CLI:

aws configure

Enter 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.html

Step 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.json

Step 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 --delete

Your site is now available at: http://my-quarto-site.com.s3-website-us-east-1.amazonaws.com

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-1

Verify domain ownership by adding DNS records shown in ACM Console.

Step 7: Configure Custom Domain

  1. In CloudFront distribution settings:
    • Alternate Domain Names (CNAMEs): Add yourdomain.com and www.yourdomain.com
    • SSL Certificate: Select your ACM certificate
  2. In your DNS provider, create a CNAME record:
    • Name: www or @
    • Value: Your CloudFront distribution domain (e.g., d123abc.cloudfront.net)

Your site is now live with HTTPS! 🔒

Invalidating CloudFront Cache

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-server

Access your app at: http://your-ec2-ip:3838/your-document.qmd

Production Tips for Shiny
  • 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: true

Setting Up Secrets

For each platform, add secrets to your GitHub repository:

  1. Go to your repo → Settings → Secrets and variables → Actions
  2. 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

CI/CD Best Practices
  1. Test before deploying: Add a test job that renders the site and checks for errors
  2. Deploy on branch merge: Only deploy from main branch
  3. Use environment variables: Store sensitive data in GitHub Secrets
  4. Monitor builds: Set up notifications for failed deployments
  5. 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/*.png

Or use Quarto’s built-in optimization in _quarto.yml:

format:
  html:
    fig-width: 8
    fig-height: 6
    fig-format: retina

2. 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: cosmo

4. 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)
Performance Testing

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:


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:

  1. 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') }}
  1. Use freeze: auto in _quarto.yml:
execute:
  freeze: auto  # Only re-run changed code

Issue 3: Large Files Exceeding Limits

Problem: Deployment fails due to file size limits.

Solutions:

  1. Use Git LFS for large files:
git lfs install
git lfs track "*.pdf"
git lfs track "*.mp4"
  1. 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 render

In 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

  1. 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)
  2. Optimize Images and Assets
    • Compress images to reduce storage and bandwidth
    • Use WebP format (smaller than JPEG/PNG)
    • Lazy-load images with loading="lazy"
  3. Implement Aggressive Caching
    • Cache static assets for long periods (1 year)
    • Use CDN to reduce origin requests
  4. Clean Up Old Versions
    • Delete old build artifacts
    • Remove unused files from storage
  5. Monitor and Alert
    • Set up billing alerts in all platforms
    • Review monthly costs and optimize
Billing Alerts Setup

AWS:

aws budgets create-budget \
    --account-id YOUR_ACCOUNT_ID \
    --budget file://budget.json \
    --notifications-with-subscribers file://notifications.json

GCP:

gcloud billing budgets create \
    --billing-account=BILLING_ACCOUNT_ID \
    --display-name="Quarto Site Budget" \
    --budget-amount=10USD

Azure: 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.sh

Multi-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 Spanish

Use 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:

  1. 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"
    }
  ]
}
  1. 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))
  );
});
  1. 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

  1. Platform Selection Matters
    • Firebase/GCP: Easiest for beginners
    • Azure: Best for Microsoft ecosystem
    • AWS: Maximum flexibility and scale
  2. Automation is Essential
    • Set up CI/CD early
    • Use GitHub Actions or platform-specific tools
    • Test before deploying
  3. Optimize from Day One
    • Compress images
    • Enable CDN
    • Implement caching
    • Monitor performance
  4. Security First
    • Always use HTTPS
    • Keep secrets in environment variables
    • Regularly update dependencies
    • Set up billing alerts
  5. Monitor and Iterate
    • Track analytics
    • Monitor uptime
    • Review costs monthly
    • Optimize based on data

Next Steps

  1. Choose your platform based on your needs and existing infrastructure
  2. Follow the step-by-step guide for your chosen platform
  3. Set up CI/CD to automate future deployments
  4. Optimize for performance and cost
  5. 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

Need Help?

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 hosting

Azure (Static Web Apps)

# Setup
az login
# Use Azure Portal to create Static Web App

# Deploy (automatic via GitHub Actions)
git push origin main

AWS (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