Deploying a Gatsby App
Gatsby is a modern, powerful static site generator built on React that combines the best of dynamic and static site generation. It features a robust plugin ecosystem, built-in GraphQL for querying data, automatic code splitting, image optimization, and blazing-fast performance out of the box. Gatsby is ideal for building high-performance websites, blogs, documentation sites, e-commerce storefronts, and any project where speed and SEO are critical. With Gatsby, you get a modern development experience, sophisticated data sourcing capabilities, and optimized production builds.
This comprehensive guide walks through deploying a Gatsby application to Klutch.sh using either Nixpacks (automatic zero-configuration deployment) or a Dockerfile (manual container control). You’ll learn how to scaffold a Gatsby project, work with pages and routing, create dynamic pages from data, use the GraphQL API, configure plugins, optimize images, set up environment variables, implement security best practices, set up monitoring, configure custom domains, and troubleshoot common issues. By the end of this guide, you’ll have a production-ready Gatsby site running on Klutch.sh’s global infrastructure with automatic HTTPS, optimized performance, and reliable hosting.
Prerequisites
- Node.js & npm (version 16+) – Download Node.js
- Gatsby CLI installed globally via npm
- Git installed locally and a GitHub account (Klutch.sh uses GitHub as the only git source)
- Klutch.sh account with access to the dashboard at klutch.sh/app
- Basic knowledge of React, GraphQL, and the Node.js ecosystem
Getting Started: Create a Gatsby App
1. Install Gatsby CLI
Install the Gatsby CLI globally:
npm install -g gatsby-cli2. Create a New Gatsby Project
Create a new Gatsby application. You can use the default starter or select from many available starters:
gatsby new my-gatsby-appcd my-gatsby-appOr use a specific starter:
gatsby new my-gatsby-app https://github.com/gatsbyjs/gatsby-starter-blogcd my-gatsby-app3. Project Structure
A typical Gatsby project has this structure:
my-gatsby-app/├── src/│ ├── components/│ │ ├── Header.js│ │ ├── Layout.js│ │ ├── Navigation.js│ │ └── Footer.js│ ├── pages/│ │ ├── index.js│ │ ├── about.js│ │ ├── blog.js│ │ └── 404.js│ ├── templates/│ │ ├── blog-post.js│ │ └── product-page.js│ ├── images/│ │ └── logo.png│ ├── styles/│ │ └── global.css│ ├── utils/│ │ └── helpers.js│ └── data/│ └── config.js├── content/│ ├── blog/│ │ ├── my-first-post.md│ │ └── another-post.md│ └── pages/│ └── about.md├── static/│ └── robots.txt├── gatsby-config.js├── gatsby-node.js├── package.json└── package-lock.json4. Run the Development Server
Start the Gatsby development server:
gatsby developThis will start a development server at http://localhost:8000 and a GraphQL IDE at http://localhost:8000/___graphql.
5. Create a Page
Create a new page at src/pages/about.js:
import React from "react"import Layout from "../components/Layout"import SEO from "../components/SEO"
export default function About() { return ( <Layout> <SEO title="About Us" description="Learn more about our company" /> <h1>About Us</h1> <p>Welcome to our Gatsby site deployed on Klutch.sh!</p> <p> Gatsby provides lightning-fast performance and excellent developer experience. </p> </Layout> )}6. Create a Layout Component
Create src/components/Layout.js:
import React from "react"import "../styles/global.css"import Navigation from "./Navigation"import Footer from "./Footer"
export default function Layout({ children }) { return ( <div style={{ display: "flex", flexDirection: "column", minHeight: "100vh" }}> <Navigation /> <main style={{ flex: 1, maxWidth: 960, margin: "0 auto", width: "100%", padding: "2rem" }}> {children} </main> <Footer /> </div> )}7. Work with Data Using GraphQL
Create a blog post query to fetch data. First, install the file system source plugin:
npm install gatsby-source-filesystem gatsby-transformer-remarkUpdate gatsby-config.js to configure plugins:
module.exports = { siteMetadata: { title: "My Gatsby Site", description: "A fast site built with Gatsby", siteUrl: "https://example-app.klutch.sh", }, plugins: [ "gatsby-plugin-react-helmet", { resolve: "gatsby-source-filesystem", options: { name: "blog", path: `${__dirname}/content/blog`, }, }, { resolve: "gatsby-source-filesystem", options: { name: "pages", path: `${__dirname}/content/pages`, }, }, "gatsby-transformer-remark", "gatsby-plugin-image", "gatsby-plugin-sharp", "gatsby-transformer-sharp", { resolve: "gatsby-plugin-manifest", options: { name: "My Gatsby Site", short_name: "Gatsby Site", start_url: "/", background_color: "#ffffff", theme_color: "#000000", display: "minimal-ui", icon: "src/images/icon.png", }, }, ],}8. Create Dynamic Pages from Markdown
Create gatsby-node.js to generate pages from markdown files:
const path = require(`path`)const { createFilePath } = require(`gatsby-source-filesystem`)
exports.onCreateNode = ({ node, getNode, actions }) => { const { createNodeField } = actions if (node.internal.type === `MarkdownRemark`) { const slug = createFilePath({ node, getNode, basePath: `blog` }) createNodeField({ node, name: `slug`, value: slug, }) }}
exports.createPages = async ({ graphql, actions }) => { const { createPage } = actions const result = await graphql(` query { allMarkdownRemark { edges { node { fields { slug } frontmatter { title } } } } } `)
if (result.errors) { throw result.errors }
result.data.allMarkdownRemark.edges.forEach(({ node }) => { createPage({ path: node.fields.slug, component: path.resolve(`./src/templates/blog-post.js`), context: { slug: node.fields.slug, }, }) })}9. Create a Blog Post Template
Create src/templates/blog-post.js:
import React from "react"import { graphql } from "gatsby"import Layout from "../components/Layout"import SEO from "../components/SEO"
export default function BlogPostTemplate(props) { const { markdownRemark } = props.data const { frontmatter, html } = markdownRemark
return ( <Layout> <SEO title={frontmatter.title} description={frontmatter.description} /> <article> <h1>{frontmatter.title}</h1> <p style={{ color: "#999", fontSize: "0.9rem" }}> {frontmatter.date} </p> <div dangerouslySetInnerHTML={{ __html: html }} /> </article> </Layout> )}
export const query = graphql` query($slug: String!) { markdownRemark(fields: { slug: { eq: $slug } }) { html frontmatter { title date(formatString: "MMMM DD, YYYY") description } } }`10. Optimize Images
Install Gatsby image plugins:
npm install gatsby-plugin-image gatsby-source-filesystem gatsby-plugin-sharpUse optimized images in components:
import { GatsbyImage, getImage } from "gatsby-plugin-image"import { graphql } from "gatsby"
export default function HeroSection({ image }) { return <GatsbyImage image={getImage(image)} alt="Hero" />}
export const query = graphql` query { file(relativePath: { eq: "hero.png" }) { childImageSharp { gatsbyImageData } } }`11. Configure Environment Variables
Create a .env.production file (or set via Klutch.sh dashboard):
GATSBY_API_URL=https://api.example.comGATSBY_SITE_URL=https://example-app.klutch.shGATSBY_ANALYTICS_ID=your-analytics-idAccess environment variables in your site:
const apiUrl = process.env.GATSBY_API_URLconst siteUrl = process.env.GATSBY_SITE_URLNote: Environment variables must start with GATSBY_ to be accessible in the browser.
12. Build and Test Locally
Build your Gatsby site for production:
gatsby buildServe the production build locally:
gatsby serveVisit http://localhost:9000 to verify the production build.
Local Production Build Test
Before deploying, thoroughly test the production build:
gatsby buildgatsby serveVerify that:
- All pages load correctly
- Navigation works as expected
- Images are optimized
- No console errors appear
- Performance is good
Visit http://localhost:9000 to test the production build.
Deploying with Nixpacks
Nixpacks automatically detects your Node.js/Gatsby application and configures build and runtime environments without requiring a Dockerfile. This is the simplest deployment method for Gatsby applications.
Prerequisites for Nixpacks Deployment
- Your Gatsby project pushed to a GitHub repository
- Valid
package.jsonwith build and start scripts - No
Dockerfilein the repository root (if one exists, Klutch.sh will use Docker instead)
Steps to Deploy with Nixpacks
-
Push Your Gatsby Project to GitHub
Initialize and push your project to GitHub:
Terminal window git initgit add .git commit -m "Initial Gatsby site"git branch -M maingit remote add origin git@github.com:YOUR_USERNAME/YOUR_REPO.gitgit push -u origin main -
Log In to Klutch.sh Dashboard
Go to klutch.sh/app and sign in with your GitHub account.
-
Create a Project
Navigate to the Projects section and create a new project for your Gatsby site.
-
Create an App
Click “Create App” and select your GitHub repository.
-
Select the Branch
Choose the branch you want to deploy (typically
main). -
Configure Traffic Type
Select HTTP as the traffic type for Gatsby (a static site generator serving HTML/assets).
-
Set the Internal Port
Set the internal port to
8000– this is the port where Gatsby serves the built site. -
Add Environment Variables (Optional)
Add any environment variables your Gatsby site requires:
GATSBY_API_URL=https://api.example.comGATSBY_SITE_URL=https://example-app.klutch.shGATSBY_ANALYTICS_ID=your-analytics-idIf you need to customize the Nixpacks build or start command, use these environment variables:
BUILD_COMMAND: Override the default build command (e.g.,gatsby build)START_COMMAND: Override the default start command (e.g.,gatsby serve)
-
Configure Compute Resources
Select your region, compute size, and number of instances based on expected traffic.
-
Deploy
Click “Create” to start the deployment. Nixpacks will automatically build and deploy your Gatsby site. Your site will be available at a URL like
https://example-app.klutch.sh.
Deploying with Docker
For more control over your deployment environment, you can use a Dockerfile. Klutch.sh automatically detects a Dockerfile in your repository root and uses it for deployment.
Creating a Dockerfile for Gatsby
Create a Dockerfile in the root of your Gatsby project:
# === Build stage ===FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./RUN npm install
COPY . .RUN npm run build
# === Runtime stage ===FROM node:18-alpine
WORKDIR /app
RUN npm install -g gatsby-cli http-server
COPY --from=builder /app/public ./public
ENV PORT=8000EXPOSE 8000
CMD ["http-server", "public", "-p", "8000", "--gzip", "-c-1"]Alternative Dockerfile Using Nginx
For a lighter-weight deployment using Nginx:
# === Build stage ===FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./RUN npm install
COPY . .RUN npm run build
# === Runtime stage (Nginx) ===FROM nginx:alpine
COPY --from=builder /app/public /usr/share/nginx/html
RUN echo 'server { \ listen 80; \ location / { \ root /usr/share/nginx/html; \ try_files $uri $uri/ $uri/index.html /index.html; \ } \ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { \ expires 1y; \ add_header Cache-Control "public, immutable"; \ } \}' > /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]Dockerfile Notes
- Builder stage: Installs dependencies and builds your Gatsby site for production.
- Runtime stage: Uses either http-server or Nginx to serve your built static files.
- Port: The
PORTenvironment variable is set to8000for http-server or80for Nginx. - Multi-stage build: Reduces final image size by excluding build tools and dev dependencies from the runtime container.
- Caching: Nginx configuration includes browser caching headers for optimal performance.
Steps to Deploy with Docker
-
Create a Dockerfile
Add the Dockerfile (shown above) to the root of your Gatsby repository.
-
Test Locally (Optional)
Build and test the Docker image locally:
Terminal window docker build -t gatsby-site:latest .docker run -p 8000:8000 gatsby-site:latestVisit http://localhost:8000 to verify.
-
Push to GitHub
Commit and push the Dockerfile and your code:
Terminal window git add Dockerfilegit commit -m "Add Dockerfile for production deployment"git push origin main -
Create an App in Klutch.sh
Go to klutch.sh/app, navigate to “Create App”, and select your repository.
-
Configure the App
- Traffic Type: Select HTTP
- Internal Port: Set to
8000(or80if using the Nginx Dockerfile) - Environment Variables: Add any required runtime variables
-
Deploy
Klutch.sh automatically detects the Dockerfile and uses it to build and deploy your site. Your site will be available at
https://example-app.klutch.sh.
Environment Variables
Define all environment variables in the Klutch.sh dashboard. Remember that environment variables must start with GATSBY_ to be accessible in the browser. Here’s a recommended set for production:
GATSBY_API_URL=https://api.example.comGATSBY_SITE_URL=https://example-app.klutch.shGATSBY_ANALYTICS_ID=your-analytics-idGATSBY_RECAPTCHA_KEY=your-recaptcha-keyNODE_ENV=productionAccessing Environment Variables
Access environment variables in your Gatsby components and pages:
const apiUrl = process.env.GATSBY_API_URLconst siteUrl = process.env.GATSBY_SITE_URLconst analyticsId = process.env.GATSBY_ANALYTICS_IDIn gatsby-config.js, environment variables are available directly:
module.exports = { siteMetadata: { siteUrl: process.env.GATSBY_SITE_URL || "http://localhost:8000", title: "My Gatsby Site", },}Persistent Storage
If your Gatsby site needs to store generated files, user uploads, or cache files, you can use persistent volumes in Klutch.sh.
Adding Persistent Volumes
- In the Klutch.sh dashboard, go to your app’s Volumes section.
- Click Add Volume.
- Set the mount path (e.g.,
/data,/uploads, or/cache). - Set the size (e.g.,
1 GiB,5 GiB). - Save and redeploy your app.
Example: Using Persistent Storage for Cache
If you’re generating static assets or caching API responses:
const fs = require("fs")const path = require("path")
exports.onPreBuild = ({ reporter }) => { const cacheDir = path.join(process.cwd(), ".cache") if (!fs.existsSync(cacheDir)) { fs.mkdirSync(cacheDir, { recursive: true }) } reporter.info(`Cache directory ready at ${cacheDir}`)}
exports.createPages = async ({ graphql, actions }) => { const { createPage } = actions const cacheDir = path.join(process.cwd(), ".cache") const dataFile = path.join(cacheDir, "api-data.json")
let data if (fs.existsSync(dataFile)) { data = JSON.parse(fs.readFileSync(dataFile, "utf-8")) } else { const response = await fetch(process.env.GATSBY_API_URL + "/data") data = await response.json() fs.writeFileSync(dataFile, JSON.stringify(data)) }
// Create pages from data data.forEach(item => { createPage({ path: `/items/${item.id}`, component: path.resolve("./src/templates/item.js"), context: { id: item.id }, }) })}Security Best Practices
1. HTTPS/SSL Enforcement
Klutch.sh automatically provides HTTPS for all deployed sites. Gatsby automatically redirects HTTP to HTTPS in production.
2. Content Security Policy
Add CSP headers to your Gatsby site by creating a vercel.json or configuring in gatsby-config.js:
module.exports = { plugins: [ { resolve: "gatsby-plugin-htaccess", options: { RewriteBase: "/", https: true, www: false, host: "example-app.klutch.sh", redirect: [ { from: "^/old-page/?$", to: "/new-page/", }, ], headers: [ "X-Frame-Options: DENY", "X-Content-Type-Options: nosniff", "X-XSS-Protection: 1; mode=block", "Referrer-Policy: strict-origin-when-cross-origin", ], }, }, ],}3. Environment Variable Protection
Never commit sensitive data to version control. Store API keys, tokens, and secrets in the Klutch.sh dashboard:
GATSBY_API_KEY=your-secret-api-keyGATSBY_ANALYTICS_ID=your-analytics-idRECAPTCHA_SECRET_KEY=your-recaptcha-secret4. Input Validation
Always validate user input if your Gatsby site collects data:
function validateEmail(email) { const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/ return re.test(email)}
function validateForm(data) { const errors = [] if (!data.name || data.name.trim().length === 0) { errors.push("Name is required") } if (!validateEmail(data.email)) { errors.push("Valid email is required") } return { isValid: errors.length === 0, errors }}5. Secure API Communication
Use HTTPS for all API calls and validate responses:
async function fetchData(endpoint) { try { const response = await fetch(`${process.env.GATSBY_API_URL}${endpoint}`) if (!response.ok) { throw new Error(`API error: ${response.statusText}`) } const data = await response.json() if (!data || typeof data !== "object") { throw new Error("Invalid API response") } return data } catch (error) { console.error("Fetch error:", error) throw error }}6. Dependency Security
Regularly audit and update dependencies:
npm auditnpm audit fixnpm update7. XSS Prevention
Be cautious with dangerouslySetInnerHTML. Only use it with trusted content:
// Safe: Markdown content processed by gatsby-transformer-remark<div dangerouslySetInnerHTML={{ __html: markdownRemark.html }} />
// Unsafe: Never do this with user input<div dangerouslySetInnerHTML={{ __html: userInput }} />Monitoring and Logging
Health Check
Gatsby automatically generates a health check endpoint. Monitor your deployment health by checking the Klutch.sh dashboard.
Performance Monitoring
Use Gatsby’s built-in performance monitoring:
export function trackPageView(pageName) { if (typeof window !== "undefined" && window.gtag) { window.gtag("pageview", { page_path: pageName, }) }}
export function trackEvent(eventName, eventData) { if (typeof window !== "undefined" && window.gtag) { window.gtag("event", eventName, eventData) }}Error Tracking
Implement error tracking with a service like Sentry:
npm install @sentry/gatsbyConfigure in gatsby-config.js:
module.exports = { plugins: [ { resolve: "@sentry/gatsby", options: { dsn: process.env.GATSBY_SENTRY_DSN, environment: process.env.NODE_ENV, tracesSampleRate: 1.0, }, }, ],}Structured Logging
Log errors to a monitoring service:
export function logError(error, context = {}) { const errorData = { timestamp: new Date().toISOString(), message: error.message, stack: error.stack, context, }
if (process.env.NODE_ENV === "production") { fetch("/api/logs", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(errorData), }).catch(err => console.error("Failed to log error:", err)) } else { console.error(JSON.stringify(errorData)) }}Custom Domains
To use a custom domain with your Klutch.sh-deployed Gatsby site:
1. Add the Domain in Klutch.sh
In the Klutch.sh dashboard, go to your app’s settings and add your custom domain (e.g., blog.example.com).
2. Update Your DNS Provider
Update your DNS records with the CNAME provided by Klutch.sh:
CNAME: blog.example.com → example-app.klutch.sh3. Configure Gatsby for Your Domain
Update your site configuration in gatsby-config.js:
module.exports = { siteMetadata: { siteUrl: process.env.GATSBY_SITE_URL || "https://blog.example.com", title: "My Blog", },}4. Wait for DNS Propagation
DNS changes can take up to 48 hours to propagate. Verify with:
nslookup blog.example.com# ordig blog.example.comOnce propagated, your Gatsby site will be accessible at your custom domain with automatic HTTPS.
Troubleshooting
Issue 1: Build Fails with “Out of Memory”
Error: JavaScript heap out of memory during build
Solutions:
- Increase Node memory:
NODE_OPTIONS=--max-old-space-size=4096 gatsby build - Set
BUILD_COMMANDenvironment variable in Klutch.sh:NODE_OPTIONS=--max-old-space-size=4096 gatsby build - Review
gatsby-config.jsfor unnecessary plugins - Optimize large datasets by using filters in GraphQL queries
- Check Klutch.sh build logs for detailed error messages
Issue 2: GraphQL Query Errors
Error: Cannot query field or Unknown fragment errors
Solutions:
- Ensure your data source plugin is installed (e.g.,
gatsby-source-filesystem) - Clear the cache:
gatsby cleanthen rebuild - Verify
gatsby-config.jsplugin configuration - Use the GraphQL IDE at
http://localhost:8000/___graphqlto test queries - Ensure markdown files have proper frontmatter
Issue 3: Images Not Optimizing
Error: Large image files, poor performance
Solutions:
- Install
gatsby-plugin-imageandgatsby-plugin-sharp - Use the
GatsbyImagecomponent instead of<img>tags - Configure image optimization in
gatsby-config.js - Ensure source images are reasonably sized (under 5MB)
- Use WebP format for modern browsers
Issue 4: Static Files Not Serving
Error: CSS, JavaScript, or other assets return 404
Solutions:
- Place static files in the
static/directory, notpublic/ - Reference static files with absolute paths:
/robots.txt - Rebuild with
gatsby clean && gatsby build - Check file permissions in Docker or build environment
- Verify the build completed successfully with all files
Issue 5: Environment Variables Not Working
Error: process.env.GATSBY_* returns undefined
Solutions:
- Ensure variable names start with
GATSBY_prefix - Set variables in the Klutch.sh dashboard before deploying
- Rebuild after changing environment variables:
gatsby clean && gatsby build - Use template literals in component renders, not just variable declarations
- Check that variables are used after Gatsby’s build phase
Issue 6: Slow Build Times
Error: Build takes more than 10-15 minutes
Solutions:
- Profile the build:
gatsby build --profile - Remove unused plugins from
gatsby-config.js - Optimize GraphQL queries to fetch only needed data
- Split large content into multiple source instances
- Use
onCreatePageselectively to avoid creating unnecessary pages - Consider increasing compute resources in Klutch.sh
Best Practices
1. Optimize Gatsby Configuration
Keep your gatsby-config.js lean by only including necessary plugins:
module.exports = { siteMetadata: { title: "My Site", siteUrl: process.env.GATSBY_SITE_URL, }, plugins: [ // Only include plugins you actually use "gatsby-plugin-image", "gatsby-plugin-sharp", "gatsby-transformer-sharp", "gatsby-plugin-react-helmet", ],}2. Use Gatsby Source Plugins Efficiently
Filter data at the source to reduce build time:
{ resolve: "gatsby-source-filesystem", options: { name: "blog", path: `${__dirname}/content/blog`, },},{ resolve: "gatsby-transformer-remark", options: { plugins: [ "gatsby-remark-images", "gatsby-remark-prismjs", ], },}3. Implement Progressive Enhancement
Ensure your site works without JavaScript:
// Use static content where possible// Avoid client-side rendering for critical content// Use `<noscript>` fallbacks4. Use Gatsby’s Internationalization (i18n)
Install and configure for multi-language support:
npm install @lekoarts/gatsby-theme-i18n5. Implement Proper Image Optimization
Always use Gatsby’s image optimization:
import { GatsbyImage, getImage } from "gatsby-plugin-image"
export default function MyComponent({ image }) { return ( <GatsbyImage image={getImage(image)} alt="Description" loading="lazy" /> )}6. Test Your GraphQL Queries
Use the GraphQL IDE to test and optimize queries before using them:
gatsby develop7. Implement Proper Caching
Configure cache headers for static assets:
// In your HTTP server or Nginx configurationexpires 1y;add_header Cache-Control "public, immutable";8. Monitor Build Performance
Regularly check build times and optimize:
gatsby build --profile9. Keep Dependencies Updated
Regularly update Gatsby and plugins:
npm outdatednpm updatenpm audit fix10. Use TypeScript
Add TypeScript support for better development experience:
npm install typescript# Create tsconfig.json and use .tsx filesVerifying Your Deployment
After deployment completes:
- Check the App URL: Visit your site at
https://example-app.klutch.shor your custom domain. - Verify All Pages Load: Navigate through different routes and pages.
- Check Performance: Use Google PageSpeed Insights to verify performance.
- Review SEO: Check with SEO checkers for proper meta tags and structure.
- Test Responsiveness: Verify the site works on mobile, tablet, and desktop.
- Check Browser Console: Open F12 and verify no errors are present.
- Review Klutch.sh Logs: Check the Klutch.sh dashboard logs for any runtime issues.
If your site doesn’t work as expected, review the troubleshooting section and check the logs in the Klutch.sh dashboard.
External Resources
- Official Gatsby Documentation
- Gatsby Plugin Library
- GraphQL Official Documentation
- Klutch.sh Official Website
- Node.js Documentation
- React Documentation
- Web Security Documentation
Deploying a Gatsby site to Klutch.sh is straightforward with Nixpacks for automatic deployment or Docker for custom environments. By following this guide, you’ve learned how to scaffold a Gatsby project, work with pages and GraphQL, create dynamic content, optimize images, configure environment variables, implement security best practices, set up monitoring, and troubleshoot common issues. Your Gatsby site is now running on Klutch.sh’s global infrastructure with automatic HTTPS, optimized performance, and reliable hosting. For additional help or questions, consult the official Gatsby documentation or contact Klutch.sh support.