Skip to content

Commit f4d83b7

Browse files
alanopsclaude
andcommitted
Add demo mode for Railway deployment without Docker
- Remove Docker installation from Dockerfile to avoid memory issues - Add graceful fallback when Docker daemon not available - Server runs in 'demo mode' showing frontend but disabling scenarios - Users can still explore the interface and see scenario descriptions - This allows Railway deployment to succeed even without Docker support Ready for Railway deployment - will work with or without Docker\! 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent eb77a08 commit f4d83b7

3 files changed

Lines changed: 64 additions & 47 deletions

File tree

Dockerfile

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,11 @@
1-
# Simplified build for Railway - no Docker build during image creation
2-
FROM node:18-slim
3-
4-
# Install only essential packages
5-
RUN apt-get update && apt-get install -y \
6-
curl \
7-
&& rm -rf /var/lib/apt/lists/*
8-
9-
# Install Docker CLI (smaller alternative)
10-
RUN curl -fsSL https://get.docker.com | sh
1+
# Ultra-minimal build for Railway
2+
FROM node:18-alpine
113

124
WORKDIR /app
135

14-
# Copy package files and install dependencies
6+
# Copy package files and install dependencies (frontend)
157
COPY package*.json ./
16-
RUN npm ci --only=production
17-
18-
COPY backend/package*.json ./backend/
19-
RUN cd backend && npm ci --only=production
8+
RUN npm ci --omit=dev --no-audit --no-fund
209

2110
# Copy and build frontend
2211
COPY src/ ./src/
@@ -28,11 +17,15 @@ COPY tsconfig.json ./
2817

2918
RUN npm run build
3019

20+
# Copy backend package files and install
21+
COPY backend/package*.json ./backend/
22+
RUN cd backend && npm ci --omit=dev --no-audit --no-fund
23+
3124
# Copy and build backend
3225
COPY backend/ ./backend/
3326
RUN cd backend && npm run build
3427

35-
# Copy scenario definitions (build images at runtime, not build time)
28+
# Copy scenario definitions (no Docker installation - will use Railway's Docker)
3629
COPY scenarios/ ./scenarios/
3730
COPY docker/ ./docker/
3831
COPY Makefile ./
@@ -45,5 +38,5 @@ WORKDIR /app/backend
4538
# Expose port
4639
EXPOSE $PORT
4740

48-
# Start backend server (builds scenario images on first use)
41+
# Start backend server (scenarios will build at runtime using Railway's Docker)
4942
CMD ["node", "dist/index.js"]

backend/index.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ async function checkDockerImage(imageName: string): Promise<boolean> {
9393
}
9494

9595
async function startScenarioContainer(socket: any, scenarioId: string) {
96+
// Check if Docker is available
97+
const dockerAvailable = await checkDockerDaemon()
98+
if (!dockerAvailable) {
99+
socket.emit('output', '\r\nDocker scenarios are not available in this deployment.\r\n')
100+
socket.emit('output', 'This is a demo version - Docker scenarios require a Docker-enabled host.\r\n')
101+
socket.emit('output', '\r\nYou can still explore the frontend interface!\r\n')
102+
socket.emit('scenario-ready')
103+
return
104+
}
105+
96106
// Map scenario IDs to Docker images
97107
const scenarioImages: Record<string, string> = {
98108
'k8s-crashloop': 'devopslearn/scenario-keycloak-crashloop',
@@ -261,28 +271,30 @@ async function buildScenarioImages() {
261271
}
262272
}
263273

264-
// Initialize server with Docker checks
274+
// Initialize server with optional Docker checks
265275
async function initializeServer() {
266-
console.log('Checking Docker daemon...')
276+
console.log('Starting DevOps Dojo server...')
277+
267278
const dockerAvailable = await checkDockerDaemon()
268279

269-
if (!dockerAvailable) {
270-
console.error('Error: Docker daemon is not accessible')
271-
console.error('Please ensure Docker is installed and running')
272-
process.exit(1)
280+
if (dockerAvailable) {
281+
console.log('Docker daemon is accessible - scenario support enabled')
282+
await ensureDockerNetwork()
283+
284+
// Build scenario images in background
285+
buildScenarioImages().catch(error => {
286+
console.log('Background scenario build failed:', error.message)
287+
})
288+
} else {
289+
console.log('Docker daemon not available - running in demo mode (no scenarios)')
290+
console.log('Frontend will work, but Docker scenarios will be unavailable')
273291
}
274292

275-
console.log('Docker daemon is accessible')
276-
277-
await ensureDockerNetwork()
278-
279-
// Build scenario images in background
280-
buildScenarioImages().catch(error => {
281-
console.log('Background scenario build failed:', error.message)
282-
})
283-
284293
server.listen(PORT, () => {
285294
console.log(`DevOps Dojo server running on port ${PORT}`)
295+
if (!dockerAvailable) {
296+
console.log('Note: Running in demo mode - Docker scenarios disabled')
297+
}
286298
})
287299
}
288300

src/server/index.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ async function checkDockerImage(imageName: string): Promise<boolean> {
9393
}
9494

9595
async function startScenarioContainer(socket: any, scenarioId: string) {
96+
// Check if Docker is available
97+
const dockerAvailable = await checkDockerDaemon()
98+
if (!dockerAvailable) {
99+
socket.emit('output', '\r\nDocker scenarios are not available in this deployment.\r\n')
100+
socket.emit('output', 'This is a demo version - Docker scenarios require a Docker-enabled host.\r\n')
101+
socket.emit('output', '\r\nYou can still explore the frontend interface!\r\n')
102+
socket.emit('scenario-ready')
103+
return
104+
}
105+
96106
// Map scenario IDs to Docker images
97107
const scenarioImages: Record<string, string> = {
98108
'k8s-crashloop': 'devopslearn/scenario-keycloak-crashloop',
@@ -261,28 +271,30 @@ async function buildScenarioImages() {
261271
}
262272
}
263273

264-
// Initialize server with Docker checks
274+
// Initialize server with optional Docker checks
265275
async function initializeServer() {
266-
console.log('Checking Docker daemon...')
276+
console.log('Starting DevOps Dojo server...')
277+
267278
const dockerAvailable = await checkDockerDaemon()
268279

269-
if (!dockerAvailable) {
270-
console.error('Error: Docker daemon is not accessible')
271-
console.error('Please ensure Docker is installed and running')
272-
process.exit(1)
280+
if (dockerAvailable) {
281+
console.log('Docker daemon is accessible - scenario support enabled')
282+
await ensureDockerNetwork()
283+
284+
// Build scenario images in background
285+
buildScenarioImages().catch(error => {
286+
console.log('Background scenario build failed:', error.message)
287+
})
288+
} else {
289+
console.log('Docker daemon not available - running in demo mode (no scenarios)')
290+
console.log('Frontend will work, but Docker scenarios will be unavailable')
273291
}
274292

275-
console.log('Docker daemon is accessible')
276-
277-
await ensureDockerNetwork()
278-
279-
// Build scenario images in background
280-
buildScenarioImages().catch(error => {
281-
console.log('Background scenario build failed:', error.message)
282-
})
283-
284293
server.listen(PORT, () => {
285294
console.log(`DevOps Dojo server running on port ${PORT}`)
295+
if (!dockerAvailable) {
296+
console.log('Note: Running in demo mode - Docker scenarios disabled')
297+
}
286298
})
287299
}
288300

0 commit comments

Comments
 (0)