Deploying Nuxt.js on Hop3¶
This guide walks you through deploying a Nuxt.js application on Hop3. By the end, you'll have a production-ready Vue.js application with server-side rendering running on your own infrastructure.
Prerequisites¶
Before you begin, ensure you have:
- A Hop3 server - Follow the Installation Guide if you haven't set one up yet
- The Hop3 CLI - Installed on your local machine
- Node.js 18+ - Install from nodejs.org
- npm - Comes with Node.js
- Git - For version control and deployment
Verify your local setup:
Step 1: Create a New Nuxt Application¶
Create the project directory and initialize:
Install Nuxt:
Create the Nuxt config:
export default defineNuxtConfig({
devtools: { enabled: false },
compatibilityDate: "2024-11-01",
nitro: {
preset: "node-server",
},
});
Update package.json with Nuxt scripts:
npm pkg set scripts.build="nuxt build" scripts.start="node .output/server/index.mjs" scripts.dev="nuxt dev"
Verify the project structure:
Step 2: Create Application Pages¶
Create the welcome page:
<template>
<div class="container">
<h1>Hello from Hop3!</h1>
<p>Your Nuxt.js application is running.</p>
<p>Current time: {{ currentTime }}</p>
<div class="links">
<NuxtLink to="/about">About</NuxtLink>
<a href="/api/health">Health Check</a>
</div>
</div>
</template>
<script setup lang="ts">
const currentTime = ref(new Date().toISOString())
onMounted(() => {
setInterval(() => {
currentTime.value = new Date().toISOString()
}, 1000)
})
</script>
<style scoped>
.container {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: linear-gradient(135deg, #00DC82 0%, #36E4DA 100%);
color: white;
text-align: center;
}
h1 {
font-size: 3rem;
margin-bottom: 1rem;
}
p {
font-size: 1.25rem;
opacity: 0.9;
}
.links {
margin-top: 2rem;
display: flex;
gap: 1rem;
}
.links a {
padding: 0.75rem 1.5rem;
background: rgba(255,255,255,0.2);
border-radius: 8px;
color: white;
text-decoration: none;
}
.links a:hover {
background: rgba(255,255,255,0.3);
}
</style>
Create an about page:
<template>
<div class="container">
<h1>About</h1>
<p>This is a Nuxt.js application deployed on Hop3.</p>
<NuxtLink to="/">Back to Home</NuxtLink>
</div>
</template>
<style scoped>
.container {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
background: #1a1a2e;
color: white;
text-align: center;
}
a {
margin-top: 2rem;
color: #00DC82;
}
</style>
Step 3: Create API Routes¶
Create API routes for health checks:
export default defineEventHandler((event) => {
return {
status: 'ok',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
}
})
export default defineEventHandler((event) => {
return {
name: 'hop3-tuto-nuxtjs',
version: '1.0.0',
node: process.version,
nuxt: '3.x',
}
})
Step 4: Configure for Production¶
Update nuxt.config.ts for production:
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
compatibilityDate: '2024-11-01',
devtools: { enabled: false },
// Nitro server configuration
nitro: {
preset: 'node-server',
},
// Runtime config
runtimeConfig: {
// Private keys (server-side only)
secretKey: process.env.SECRET_KEY || 'dev-secret',
// Public keys (exposed to client)
public: {
apiBase: process.env.API_BASE || '/api',
},
},
// App configuration
app: {
head: {
title: 'My Nuxt App',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
],
},
},
})
Step 5: Build and Verify¶
Build the application:
Verify the build output:
Step 6: Create Deployment Configuration¶
Create a Procfile¶
# Pre-build: Install dependencies and build
prebuild: npm install && npm run build
# Main web process
web: node .output/server/index.mjs
Create hop3.toml¶
[metadata]
id = "hop3-tuto-nuxtjs"
version = "1.0.0"
title = "My Nuxt.js Application"
[build]
before-build = ["npm install", "npm run build"]
packages = ["nodejs", "npm"]
[run]
start = "node .output/server/index.mjs"
[env]
NODE_ENV = "production"
NITRO_PORT = "$PORT"
[port]
web = 3000
[healthcheck]
path = "/api/up"
timeout = 30
interval = 60
Verify the deployment files:
Step 7: Initialize Git Repository¶
# Dependencies
node_modules/
# Build output
.output/
.nuxt/
dist/
# Environment
.env
.env.*
!.env.example
# IDE
.idea/
.vscode/
*.swp
# OS
.DS_Store
# Logs
*.log
Step 8: Deploy to Hop3¶
The following steps require a Hop3 server.
Initialize (First Time Only)¶
Deploy¶
Deploy the application (first deployment creates the app):
Set Hostname¶
Configure the hostname for nginx proxy:
Wait for Process Stop¶
Wait for the previous deployment to fully stop:
Apply Configuration¶
Redeploy to apply the hostname configuration:
Verify Deployment¶
Managing Your Application¶
Environment Variables¶
hop3 config:set hop3-tuto-nuxtjs NUXT_PUBLIC_API_BASE=https://api.example.com
hop3 config:set hop3-tuto-nuxtjs NUXT_SECRET_KEY=$(openssl rand -hex 32)
View Logs¶
Advanced Configuration¶
Adding a Database (PostgreSQL with Prisma)¶
Server Middleware¶
// server/middleware/auth.ts
export default defineEventHandler((event) => {
const authHeader = getHeader(event, 'authorization')
if (event.path.startsWith('/api/protected') && !authHeader) {
throw createError({ statusCode: 401, message: 'Unauthorized' })
}
})
Static Site Generation¶
For fully static sites:
Troubleshooting¶
Build Failures¶
- Check Node.js version matches locally and on server
- Ensure all dependencies are in
dependencies(notdevDependencies)
Runtime Errors¶
- Check
NITRO_PORTis set correctly - Verify
.outputdirectory exists after build
Example Files¶
Complete hop3.toml¶
[metadata]
id = "hop3-tuto-nuxtjs"
version = "1.0.0"
title = "My Nuxt.js Application"
[build]
before-build = ["npm install", "npm run build"]
packages = ["nodejs"]
[run]
start = "node .output/server/index.mjs"
[env]
NODE_ENV = "production"
NITRO_PORT = "$PORT"
[port]
web = 3000
[healthcheck]
path = "/api/up"
timeout = 30
interval = 60
[[provider]]
name = "postgres"
plan = "standard"