Deployment Guide
This comprehensive guide covers all aspects of deploying Neutrino static sites, from preparation and build optimization to various hosting platforms and deployment strategies.
Pre-Deployment Preparation
Build Verification
Before deploying, ensure your site builds correctly:
# Test production buildnpm run build
# Verify build outputls -la _site/
# Check for build errorsnpm run build 2>&1 | grep -i error
Content Validation
Check for Draft Content
# Find any draft content that shouldn't be publishedgrep -r "draft: true" src/content/
# Remove or set to false before deploymentfind src/content -name "*.md" -exec sed -i 's/draft: true/draft: false/g' {} \;
Validate Frontmatter
# Check for missing required fieldsfind src/content -name "*.md" -exec grep -L "title:" {} \;find src/content -name "*.md" -exec grep -L "date:" {} \;
Environment Configuration
Production Environment Variables
Create a production .env
file:
NEUTRINO_CONTENT_PATH=/path/to/production/contentTHEME=neutrino-electron-coreNODE_ENV=production
Site Configuration Update
Update src/_data/site.json
for production:
{ "name": "Your Production Site", "url": "https://yourdomain.com", "description": "Your production site description", "logo": "/assets/images/your-logo.svg", "favicon": "/assets/images/your-favicon.png", "theme": "neutrino-electron-core", "defaultVisualTheme": "dark", "contentPath": "${NEUTRINO_CONTENT_PATH}"}
Build Optimization
Production Build Script
Create an optimized build script:
{ "scripts": { "build:prod": "NODE_ENV=production npm run build", "build:css:prod": "NODE_ENV=production npm run build:css", "prebuild": "npm run build:css:prod", "postbuild": "npm run optimize:assets" }}
Asset Optimization
CSS Optimization
// In scripts/build.js - Production optimizationconst command = `sass --no-source-map --style=compressed src/sass:src/assets/css src/themes/${theme}:src/assets/css`;
Image Optimization
// In eleventy.config.jseleventyConfig.addNunjucksAsyncShortcode("image", async function(src, alt, sizes) { let stats = await Image(src, { widths: [300, 600, 900, 1200], formats: ["webp", "jpeg"], outputDir: "./_site/assets/images/", urlPath: "/assets/images/", sharpWebpOptions: { quality: 80 }, sharpJpegOptions: { quality: 80 } });
return `<picture> <source srcset="${stats.webp.map(stat => stat.srcset).join(', ')}" type="image/webp"> <img src="${stats.jpeg[0].url}" alt="${alt}" sizes="${sizes}" loading="lazy"> </picture>`;});
Performance Optimization
Critical CSS Extraction
// Extract critical CSS for above-the-fold content@import 'critical/base';@import 'critical/header';@import 'critical/navigation';
Lazy Loading Implementation
<!-- In templates --><img src="/assets/images/placeholder.jpg" data-src="/assets/images/actual-image.jpg" alt="Description" loading="lazy" class="lazy-load">
Static Hosting Platforms
Netlify Deployment
Automatic Deployment
-
Connect Repository:
- Go to Netlify
- Click "New site from Git"
- Connect your GitHub/GitLab repository
-
Build Settings:
Build command: npm run buildPublish directory: _siteNode version: 18 -
Environment Variables:
NEUTRINO_CONTENT_PATH=./contentTHEME=neutrino-electron-coreNODE_ENV=production
Netlify Configuration File
Create netlify.toml
:
[build] command = "npm run build" publish = "_site"
[build.environment] NODE_VERSION = "18" NPM_VERSION = "9"
[[redirects]] from = "/admin/*" to = "/admin/index.html" status = 200
[[headers]] for = "/assets/*" [headers.values] Cache-Control = "public, max-age=31536000"
[[headers]] for = "/*.html" [headers.values] Cache-Control = "public, max-age=0, must-revalidate"
Form Handling (Decap CMS)
[[redirects]] from = "/admin/*" to = "/admin/index.html" status = 200
# Handle form submissions[[redirects]] from = "/.netlify/functions/*" to = "/.netlify/functions/:splat" status = 200
Vercel Deployment
Automatic Deployment
-
Connect Repository:
- Go to Vercel
- Import your GitHub repository
- Configure build settings
-
Build Configuration:
Create vercel.json
:
{ "buildCommand": "npm run build", "outputDirectory": "_site", "framework": null, "installCommand": "npm install", "devCommand": "npm run serve", "env": { "NEUTRINO_CONTENT_PATH": "./content", "THEME": "neutrino-electron-core", "NODE_ENV": "production" }, "headers": [ { "source": "/assets/(.*)", "headers": [ { "key": "Cache-Control", "value": "public, max-age=31536000" } ] } ], "rewrites": [ { "source": "/admin/(.*)", "destination": "/admin/index.html" } ]}
Vercel Functions (Optional)
For dynamic functionality, create api/
directory:
export default function handler(req, res) { if (req.method === 'POST') { // Handle form submission res.status(200).json({ message: 'Form submitted successfully' }); } else { res.status(405).json({ message: 'Method not allowed' }); }}
GitHub Pages Deployment
GitHub Actions Workflow
Create .github/workflows/deploy.yml
:
name: Deploy to GitHub Pages
on: push: branches: [ main ] pull_request: branches: [ main ]
jobs: build-and-deploy: runs-on: ubuntu-latest
steps: - name: Checkout uses: actions/checkout@v3
- name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' cache: 'npm'
- name: Install dependencies run: npm ci
- name: Build site run: npm run build env: NEUTRINO_CONTENT_PATH: ./content THEME: neutrino-electron-core NODE_ENV: production
- name: Deploy to GitHub Pages uses: peaceiris/actions-gh-pages@v3 if: github.ref == 'refs/heads/main' with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./_site
Repository Settings
- Go to repository Settings → Pages
- Source: GitHub Actions
- Enable the workflow
Cloudflare Pages
Automatic Deployment
-
Connect Repository:
- Go to Cloudflare Pages
- Connect your Git repository
-
Build Settings:
Framework preset: NoneBuild command: npm run buildBuild output directory: _siteRoot directory: (leave empty) -
Environment Variables:
NEUTRINO_CONTENT_PATH=./contentTHEME=neutrino-electron-coreNODE_ENV=production
Cloudflare Configuration
Create _headers
file in _site
directory:
/assets/* Cache-Control: public, max-age=31536000
Cache-Control: public, max-age=0, must-revalidate
/admin/* Cache-Control: public, max-age=0, must-revalidate
Create _redirects
file:
/admin/* /admin/index.html 200
Traditional Web Hosting
Shared Hosting (cPanel, etc.)
Manual Upload
-
Build the site:
Terminal window npm run build -
Upload files:
- Use FTP/SFTP client
- Upload contents of
_site/
topublic_html/
- Maintain directory structure
-
Configure .htaccess:
# .htaccess for Apache serversRewriteEngine On# Handle admin routesRewriteRule ^admin/(.*)$ /admin/index.html [L]# Cache static assets<FilesMatch "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$">ExpiresActive OnExpiresDefault "access plus 1 year"</FilesMatch># Compress files<IfModule mod_deflate.c>AddOutputFilterByType DEFLATE text/plainAddOutputFilterByType DEFLATE text/htmlAddOutputFilterByType DEFLATE text/xmlAddOutputFilterByType DEFLATE text/cssAddOutputFilterByType DEFLATE application/xmlAddOutputFilterByType DEFLATE application/xhtml+xmlAddOutputFilterByType DEFLATE application/rss+xmlAddOutputFilterByType DEFLATE application/javascriptAddOutputFilterByType DEFLATE application/x-javascript</IfModule>
VPS/Dedicated Server
Nginx Configuration
server { listen 80; server_name yourdomain.com www.yourdomain.com; root /var/www/yourdomain.com/_site; index index.html;
# Gzip compression gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# Cache static assets location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; add_header Cache-Control "public, immutable"; }
# Handle admin routes location /admin { try_files $uri $uri/ /admin/index.html; }
# Handle all other routes location / { try_files $uri $uri/ =404; }
# Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-XSS-Protection "1; mode=block" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer-when-downgrade" always; add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;}
SSL Configuration (Let's Encrypt)
# Install Certbotsudo apt install certbot python3-certbot-nginx
# Obtain SSL certificatesudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Auto-renewalsudo crontab -e# Add: 0 12 * * * /usr/bin/certbot renew --quiet
CI/CD Pipeline
GitHub Actions (Advanced)
Multi-Environment Deployment
name: Deploy
on: push: branches: [ main, staging ] pull_request: branches: [ main ]
env: NODE_VERSION: '18'
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3
- name: Setup Node.js uses: actions/setup-node@v3 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm'
- name: Install dependencies run: npm ci
- name: Build site run: npm run build env: NEUTRINO_CONTENT_PATH: ./content THEME: neutrino-electron-core NODE_ENV: production
- name: Upload build artifacts uses: actions/upload-artifact@v3 with: name: site-build path: _site/
deploy-staging: needs: build runs-on: ubuntu-latest if: github.ref == 'refs/heads/staging' steps: - name: Download build artifacts uses: actions/download-artifact@v3 with: name: site-build
- name: Deploy to staging run: | # Deploy to staging server rsync -avz --delete _site/ user@staging-server:/var/www/staging/
deploy-production: needs: build runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' steps: - name: Download build artifacts uses: actions/download-artifact@v3 with: name: site-build
- name: Deploy to production run: | # Deploy to production server rsync -avz --delete _site/ user@production-server:/var/www/production/
GitLab CI/CD
GitLab Pipeline
Create .gitlab-ci.yml
:
stages: - build - deploy
variables: NODE_VERSION: "18"
build: stage: build image: node:${NODE_VERSION} cache: paths: - node_modules/ script: - npm ci - npm run build artifacts: paths: - _site/ expire_in: 1 hour
deploy_staging: stage: deploy image: alpine:latest before_script: - apk add --no-cache rsync openssh-client - eval $(ssh-agent -s) - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - mkdir -p ~/.ssh - chmod 700 ~/.ssh - ssh-keyscan $STAGING_SERVER >> ~/.ssh/known_hosts script: - rsync -avz --delete _site/ $STAGING_USER@$STAGING_SERVER:/var/www/staging/ only: - staging
deploy_production: stage: deploy image: alpine:latest before_script: - apk add --no-cache rsync openssh-client - eval $(ssh-agent -s) - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - - mkdir -p ~/.ssh - chmod 700 ~/.ssh - ssh-keyscan $PRODUCTION_SERVER >> ~/.ssh/known_hosts script: - rsync -avz --delete _site/ $PRODUCTION_USER@$PRODUCTION_SERVER:/var/www/production/ only: - main when: manual
Content Management System Integration
Decap CMS Deployment
Git Gateway Setup
-
Enable Git Gateway:
- Go to your Netlify site settings
- Enable "Git Gateway" in Identity settings
- Configure OAuth providers
-
Update CMS Configuration:
backend: name: git-gateway branch: main
# For productionpublish_mode: editorial_workflow
collections: - name: "posts" label: "Blog Posts" folder: "src/content/posts" create: true slug: "{{slug}}" fields: - {label: "Title", name: "title", widget: "string"} - {label: "Date", name: "date", widget: "datetime"} - {label: "Author", name: "author", widget: "string"} - {label: "Description", name: "description", widget: "text"} - {label: "Tags", name: "tags", widget: "list"} - {label: "Draft", name: "draft", widget: "boolean", default: true} - {label: "Body", name: "body", widget: "markdown"}
Authentication Setup
# Identity settings in Netlifyidentity: enabled: true external: - provider: github enabled: true client_id: ${GITHUB_CLIENT_ID} client_secret: ${GITHUB_CLIENT_SECRET}
Headless CMS Integration
Strapi Integration
// Fetch content from Strapiasync function getStrapiContent() { const response = await fetch('https://your-strapi-instance.com/api/posts'); const data = await response.json(); return data;}
// In eleventy.config.jseleventyConfig.addGlobalData("strapiPosts", async () => { return await getStrapiContent();});
Contentful Integration
const contentful = require('contentful');
const client = contentful.createClient({ space: process.env.CONTENTFUL_SPACE_ID, accessToken: process.env.CONTENTFUL_ACCESS_TOKEN});
async function getContentfulPosts() { const entries = await client.getEntries({ content_type: 'blogPost' }); return entries.items;}
Performance Monitoring
Analytics Integration
Google Analytics 4
<!-- In base.njk template --><script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"></script><script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'GA_MEASUREMENT_ID');</script>
Core Web Vitals Monitoring
import {getCLS, getFID, getFCP, getLCP, getTTFB} from 'web-vitals';
function sendToAnalytics(metric) { // Send to your analytics service gtag('event', metric.name, { value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value), event_category: 'Web Vitals', event_label: metric.id, non_interaction: true, });}
getCLS(sendToAnalytics);getFID(sendToAnalytics);getFCP(sendToAnalytics);getLCP(sendToAnalytics);getTTFB(sendToAnalytics);
Error Monitoring
Sentry Integration
import * as Sentry from "@sentry/browser";
Sentry.init({ dsn: "YOUR_SENTRY_DSN", environment: process.env.NODE_ENV});
Security Considerations
Content Security Policy
<!-- In base.njk template --><meta http-equiv="Content-Security-Policy" content=" default-src 'self'; script-src 'self' 'unsafe-inline' https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://www.google-analytics.com;">
Security Headers
# Nginx security headersadd_header X-Frame-Options "SAMEORIGIN" always;add_header X-XSS-Protection "1; mode=block" always;add_header X-Content-Type-Options "nosniff" always;add_header Referrer-Policy "no-referrer-when-downgrade" always;add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Troubleshooting
Common Deployment Issues
Build Failures
# Check Node.js versionnode --version
# Clear npm cachenpm cache clean --force
# Reinstall dependenciesrm -rf node_modules package-lock.jsonnpm install
# Check for syntax errorsnpm run build 2>&1 | grep -i error
Content Not Updating
# Check content path configurationecho $NEUTRINO_CONTENT_PATH
# Verify content files existls -la src/content/
# Check for draft contentgrep -r "draft: true" src/content/
CSS Not Loading
# Check CSS compilationnpm run build:css
# Verify CSS files existls -la src/assets/css/
# Check for SCSS syntax errorssass --check src/sass/core.scss
Performance Issues
Large Bundle Sizes
# Analyze bundle sizenpm install -g webpack-bundle-analyzernpx webpack-bundle-analyzer _site/assets/css/*.css
# Check for unused CSSnpm install -g purgecsspurgecss --css _site/assets/css/*.css --content _site/**/*.html
Slow Loading Times
# Check image optimizationls -lh _site/assets/images/
# Verify compressiongzip -l _site/assets/css/*.css
# Test with Lighthousenpx lighthouse http://localhost:8080 --output html
Best Practices
Pre-Deployment Checklist
- Build verification: Site builds without errors
- Content validation: No draft content in production
- Asset optimization: Images and CSS optimized
- Performance testing: Lighthouse score > 90
- Security headers: CSP and security headers configured
- Analytics setup: Tracking configured
- Backup strategy: Content and configuration backed up
- Monitoring: Error tracking and performance monitoring
Post-Deployment Verification
- Site accessibility: All pages load correctly
- Form functionality: Contact forms and CMS work
- Mobile responsiveness: Site works on all devices
- SEO verification: Meta tags and structured data
- Performance check: Core Web Vitals within targets
- Security scan: No vulnerabilities detected
Maintenance
- Regular updates: Keep dependencies updated
- Content backups: Regular content backups
- Performance monitoring: Track Core Web Vitals
- Security updates: Monitor for security patches
- Analytics review: Regular performance analysis
This comprehensive deployment guide covers all aspects of deploying Neutrino sites, from simple static hosting to advanced CI/CD pipelines and performance optimization.