Building Your SaaS Website with Hugo: From Setup to Oracle Cloud Deployment
Introduction
When building a SaaS application, establishing a strong online presence is crucial. We need a platform where we can publish information about our product, host documentation, and showcase our offerings to potential customers.
Choosing the Right Tool
After researching available options, I narrowed it down to two compelling choices. The first was Ghost - a powerful platform, but it comes with subscription costs. At this stage of my project, minimizing expenses is essential since my SaaS isn’t generating revenue yet. The second option was Hugo, which proved to be the perfect fit.
Why Hugo?
Hugo is a lightning-fast static site generator that allows you to create websites, blogs, and documentation using simple Markdown files. Instead of relying on databases or complex frameworks, Hugo converts .md files into production-ready HTML that can be easily hosted on platforms like GitHub Pages, Netlify, Vercel, or in our case - Oracle Cloud.
Key advantages:
- Blazing fast build times - Sites compile in milliseconds
- Zero runtime dependencies - Pure static files
- Markdown-first approach - Easy content creation
- Extensible - Rich ecosystem of themes and plugins
- Cost-effective - Free hosting options available
Our Deployment Strategy
Our goal is to deploy the website in a Docker container on Oracle Cloud’s Always Free tier, providing a robust hosting solution without ongoing costs.
Step-by-Step Installation Guide
Prerequisites
I’m working on Windows with Chocolatey package manager installed. We’ll use it to install Hugo CLI efficiently.
Note: If you don’t have Chocolatey installed, visit chocolatey.org for installation instructions.
Installing Hugo Extended
Open a terminal with administrator privileges and run:
choco install hugo-extended -y
Why Hugo Extended? This is the extended version of Hugo, which is essential for our use case. Hugo Extended includes:
- TailwindCSS integration - Modern utility-first CSS framework
- Built-in Sass/SCSS compiler - Advanced CSS preprocessing
- PostCSS support - CSS transformation and optimization
- WebP image processing - Modern image format support
These features are required by most modern Hugo themes, including the one we’ll be using.
Creating a New Hugo Project
Initialize your new Hugo site:
hugo new site EasyDeploySaaS
cd EasyDeploySaaS
Adding the Theme
We’ll use the hugo-saasify-theme, a modern theme designed specifically for SaaS websites:
git init
git submodule add https://github.com/lukasztomalczyk/hugo-saasify-theme themes/hugo-saasify-theme
Initial Configuration
Create a basic configuration file (hugo.toml):
baseURL = "https://your-domain.com"
title = "Your SaaS Name"
theme = "hugo-saasify-theme"
[module]
[module.hugoVersion]
extended = true
min = "0.80.0"
Testing Your Setup
Start the development server to verify everything works:
hugo server -D
Your site should now be available at http://localhost:1313.
Building Your Site
Once you have your content ready, build the static files:
hugo --minify
This generates optimized static files in the public/ directory, ready for deployment.
Deploying to Oracle Cloud
Now let’s deploy our Hugo site to Oracle Cloud using Docker and Azure Pipelines.
Creating the Dockerfile
Create a Dockerfile in your project root with multi-stage build:
# Stage 1: Build Hugo + TailwindCSS
FROM hugomods/hugo:debian-std-exts-0.152.2 AS builder
WORKDIR /src
COPY . .
# Install theme dependencies (TailwindCSS / PostCSS)
RUN npm install
# Build CSS/JS and generate Hugo site
RUN npm run build
# Stage 2: Serve with Nginx
FROM nginx:alpine
# Remove default content
RUN rm -rf /usr/share/nginx/html/*
# Copy built Hugo files
COPY --from=builder /src/public /usr/share/nginx/html
# Copy custom Nginx configuration
COPY nginx.conf /etc/nginx/nginx.conf
# Expose custom port
EXPOSE 8091
# Start Nginx
CMD ["nginx", "-g", "daemon off;"]
Azure Pipeline Configuration
We’ll use Azure Pipelines for automated deployment. You’ll need to:
- Set up SSH connector in Azure DevOps
- Configure server connection details
Pipeline 1: Build and Push Docker Image
Create website-azure-pipeline-compile.yml:
trigger: none
parameters:
- name: repository
displayName: Repository
type: string
default: 'Production'
values:
- Production
- Test
variables:
docker_tag: '${{ parameters.repository }}'
app_name: 'EasyDeployWebsite'
app_name_lower: 'easy-deploy-website'
resources:
- repo: self
stages:
- stage: Build
displayName: Build Multiarch Docker Image
jobs:
- job: Build
displayName: Build and Push Multiarch Image
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
submodules: true
# Docker Hub login
- task: Docker@2
displayName: Docker Login
inputs:
command: login
containerRegistry: 'hub.docker.com'
# Setup QEMU for ARM emulation
- script: |
docker run --rm --privileged tonistiigi/binfmt --install all
displayName: Setup QEMU for multiarch
# Create buildx builder
- script: |
docker buildx create --use --name mybuilder
docker buildx inspect --bootstrap
displayName: Setup Docker Buildx
# Build and push multiarch image
- script: |
docker buildx build \
--platform linux/arm64 \
-f $(Build.SourcesDirectory)/src/Dockerfile \
-t lukasztomalczyk/sp-craft.$(app_name_lower)-arm64:$(Build.BuildId) \
-t lukasztomalczyk/sp-craft.$(app_name_lower)-arm64:$(docker_tag) \
$(Build.SourcesDirectory)/src \
--push
displayName: Build and Push Multiarch Docker Image
Pipeline 2: Deploy to Server
Create website-azure-pipeline-install.yml:
trigger: none
parameters:
- name: server
displayName: Environment
type: string
default: 'Production'
values:
- Production
- Test
variables:
- name: environment
value: '${{ parameters.server }}'
- name: app_name
value: 'EasyDeployWebsite'
- name: server_username
value: 'opc'
resources:
- repo: self
stages:
- stage: Deploy
displayName: Deploy to Server
jobs:
- job: Deploy
displayName: Deploy and Run
pool:
vmImage: ubuntu-latest
steps:
- task: SSH@0
displayName: Create required folders
inputs:
sshEndpoint: 'SSH-$(environment)'
runOptions: 'commands'
commands: >
mkdir -p /home/$(server_username)/$(environment)/SpCraft.$(app_name)
readyTimeout: '20000'
- task: replacetokens@6
displayName: Replace tokens in docker-compose
inputs:
root: '$(Build.SourcesDirectory)/src'
sources: 'docker-compose.yml'
tokenPattern: 'doublebraces'
missingVarAction: 'keep'
- task: CopyFilesOverSSH@0
displayName: Copy docker-compose.yml
inputs:
sshEndpoint: 'SSH-$(environment)'
sourceFolder: '$(Build.SourcesDirectory)/src'
contents: |
docker-compose.yml
targetFolder: >
/home/$(server_username)/$(environment)/SpCraft.$(app_name)
readyTimeout: '20000'
- task: SSH@0
displayName: Stop existing container
continueOnError: true
inputs:
sshEndpoint: 'SSH-$(environment)'
runOptions: 'commands'
commands: >
docker-compose
-f /home/$(server_username)/$(environment)/SpCraft.$(app_name)/docker-compose.yml
down >/dev/null 2>&1
readyTimeout: '20000'
- task: SSH@0
displayName: Pull latest images
continueOnError: true
inputs:
sshEndpoint: 'SSH-$(environment)'
runOptions: 'commands'
commands: >
docker-compose
-f /home/$(server_username)/$(environment)/SpCraft.$(app_name)/docker-compose.yml
pull >/dev/null 2>&1
readyTimeout: '20000'
- task: SSH@0
displayName: Start container
inputs:
sshEndpoint: 'SSH-$(environment)'
runOptions: 'commands'
commands: >
docker-compose
-f /home/$(server_username)/$(environment)/SpCraft.$(app_name)/docker-compose.yml
up -d >/dev/null 2>&1
readyTimeout: '20000'
Environment Management
Note: This setup includes environment separation (Production/Test). For simpler deployments, you can:
- Remove the
parameterssection - Use fixed values instead of variables
- Skip environment-specific configurations
Key Benefits
- ARM64 Architecture Support - Optimized for Oracle Cloud ARM instances
- Multi-stage Build - Smaller production images
- Automated Deployment - Zero-downtime deployments
- Environment Separation - Production and test environments
Conclusion
Hugo Extended provides an excellent foundation for building fast, scalable SaaS websites. The static nature of Hugo sites ensures lightning-fast performance and minimal server resource usage, making it perfect for modern web applications.
Next Steps
- Customize your theme to match your brand
- Create compelling content for your target audience
- Set up deployment to your preferred hosting platform
- Implement analytics and SEO optimization