Add Alpine Linux service setup and CI/CD deployment
- Add ALPINE_SERVICE.md with full setup instructions - Add start.sh script for OpenRC service - Update build.yml for deployment to /opt/immersive - Configure proper permissions for immersive user - Add Gitea runner setup instructions with sudo config - Add .env.production to gitignore 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
33019c116b
commit
e2216c17e8
39
.github/workflows/build.yml
vendored
39
.github/workflows/build.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Build
|
||||
name: Build and Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
@ -9,6 +9,7 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: linux_amd64
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
@ -16,13 +17,43 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
timeout-minutes: 5
|
||||
|
||||
- name: Build Front End
|
||||
run: npm run build
|
||||
timeout-minutes: 10
|
||||
env:
|
||||
NODE_OPTIONS: '--max-old-space-size=4096'
|
||||
|
||||
- name: Restart Server
|
||||
- name: Stop Service
|
||||
run: |
|
||||
# Start server in background with nohup
|
||||
nohup npm run start > /tmp/immersive.log 2>&1 &
|
||||
sudo rc-service immersive stop || true
|
||||
|
||||
- name: Deploy to /opt/immersive
|
||||
run: |
|
||||
# Remove old deployment (keep data directory if it exists)
|
||||
rm -rf /opt/immersive.old
|
||||
[ -d /opt/immersive ] && mv /opt/immersive /opt/immersive.old
|
||||
|
||||
# Copy built files to target
|
||||
mkdir -p /opt/immersive
|
||||
cp -r . /opt/immersive/
|
||||
|
||||
# Remove unnecessary directories
|
||||
rm -rf /opt/immersive/.git /opt/immersive/.github
|
||||
|
||||
# Restore data directory from old deployment if it existed
|
||||
[ -d /opt/immersive.old/data ] && mv /opt/immersive.old/data /opt/immersive/
|
||||
|
||||
# Clean up old deployment
|
||||
rm -rf /opt/immersive.old
|
||||
|
||||
# Set permissions on start.sh
|
||||
chmod +x /opt/immersive/start.sh
|
||||
|
||||
# Set ownership to immersive user
|
||||
sudo chown -R immersive:immersive /opt/immersive
|
||||
|
||||
- name: Start Service
|
||||
run: |
|
||||
sudo rc-service immersive start
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -26,3 +26,4 @@ dist-ssr
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
/data/
|
||||
/.env.production
|
||||
|
||||
280
ALPINE_SERVICE.md
Normal file
280
ALPINE_SERVICE.md
Normal file
@ -0,0 +1,280 @@
|
||||
# Alpine Linux Service Setup
|
||||
|
||||
This guide covers installing and running Immersive as a service on Alpine Linux using OpenRC.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
```bash
|
||||
# Update packages
|
||||
apk update
|
||||
|
||||
# Install Node.js 18+ and npm
|
||||
apk add nodejs npm
|
||||
|
||||
# Install build dependencies (required for native modules like leveldown)
|
||||
apk add python3 make g++ git
|
||||
|
||||
# Verify Node version (must be >= 18)
|
||||
node --version
|
||||
```
|
||||
|
||||
## Create Service User
|
||||
|
||||
Create a dedicated user to run the service (security best practice):
|
||||
|
||||
```bash
|
||||
# Create immersive group and user (no login shell, no home directory)
|
||||
addgroup -S immersive
|
||||
adduser -S -G immersive -H -s /sbin/nologin immersive
|
||||
|
||||
# Create directories with proper ownership
|
||||
mkdir -p /opt/immersive
|
||||
mkdir -p /var/log/immersive
|
||||
mkdir -p /var/run/immersive
|
||||
|
||||
chown -R immersive:immersive /opt/immersive
|
||||
chown -R immersive:immersive /var/log/immersive
|
||||
chown -R immersive:immersive /var/run/immersive
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Create application directory
|
||||
mkdir -p /opt/immersive
|
||||
cd /opt/immersive
|
||||
|
||||
# Clone or copy the application
|
||||
git clone <your-repo-url> .
|
||||
# OR copy files manually
|
||||
|
||||
# Install dependencies
|
||||
npm ci --production=false
|
||||
|
||||
# Build the application
|
||||
NODE_OPTIONS='--max-old-space-size=4096' npm run build
|
||||
|
||||
# Copy Havok physics WASM (if not already done by build)
|
||||
npm run havok
|
||||
|
||||
# Create data directory for PouchDB
|
||||
mkdir -p /opt/immersive/data
|
||||
|
||||
# Set ownership to immersive user
|
||||
chown -R immersive:immersive /opt/immersive
|
||||
```
|
||||
|
||||
## Start Script
|
||||
|
||||
The `start.sh` script is included in the repository. After deployment, ensure it's executable:
|
||||
|
||||
```bash
|
||||
chmod +x /opt/immersive/start.sh
|
||||
```
|
||||
|
||||
The script sets up the environment and starts the Node.js server, logging output to `/var/log/immersive/`.
|
||||
|
||||
## OpenRC Service
|
||||
|
||||
Create `/etc/init.d/immersive`:
|
||||
|
||||
```bash
|
||||
#!/sbin/openrc-run
|
||||
|
||||
name="immersive"
|
||||
description="Immersive WebXR Diagramming Application"
|
||||
|
||||
command="/opt/immersive/start.sh"
|
||||
command_user="immersive:immersive"
|
||||
command_background="yes"
|
||||
pidfile="/var/run/immersive/immersive.pid"
|
||||
|
||||
directory="/opt/immersive"
|
||||
output_log="/var/log/immersive/app.log"
|
||||
error_log="/var/log/immersive/error.log"
|
||||
|
||||
depend() {
|
||||
need net
|
||||
after firewall
|
||||
}
|
||||
|
||||
start_pre() {
|
||||
checkpath --directory --owner immersive:immersive --mode 0755 /var/log/immersive
|
||||
checkpath --directory --owner immersive:immersive --mode 0755 /var/run/immersive
|
||||
checkpath --file --owner immersive:immersive --mode 0644 /var/log/immersive/app.log
|
||||
checkpath --file --owner immersive:immersive --mode 0644 /var/log/immersive/error.log
|
||||
}
|
||||
```
|
||||
|
||||
Make it executable and enable:
|
||||
|
||||
```bash
|
||||
chmod +x /etc/init.d/immersive
|
||||
rc-update add immersive default
|
||||
```
|
||||
|
||||
## Service Management
|
||||
|
||||
```bash
|
||||
# Start the service
|
||||
rc-service immersive start
|
||||
|
||||
# Stop the service
|
||||
rc-service immersive stop
|
||||
|
||||
# Restart the service
|
||||
rc-service immersive restart
|
||||
|
||||
# Check status
|
||||
rc-service immersive status
|
||||
|
||||
# View logs
|
||||
tail -f /var/log/immersive/app.log
|
||||
tail -f /var/log/immersive/error.log
|
||||
```
|
||||
|
||||
## Log Rotation
|
||||
|
||||
Create `/etc/logrotate.d/immersive`:
|
||||
|
||||
```
|
||||
/var/log/immersive/*.log {
|
||||
daily
|
||||
rotate 7
|
||||
compress
|
||||
delaycompress
|
||||
missingok
|
||||
notifempty
|
||||
create 0644 immersive immersive
|
||||
postrotate
|
||||
rc-service immersive restart > /dev/null 2>&1 || true
|
||||
endscript
|
||||
}
|
||||
```
|
||||
|
||||
Install logrotate if not present:
|
||||
|
||||
```bash
|
||||
apk add logrotate
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Create `/opt/immersive/.env.production` for production settings:
|
||||
|
||||
```bash
|
||||
# Server
|
||||
NODE_ENV=production
|
||||
PORT=3001
|
||||
|
||||
# Auth0 (if using authentication)
|
||||
# VITE_AUTH0_DOMAIN=your-domain.auth0.com
|
||||
# VITE_AUTH0_CLIENT_ID=your-client-id
|
||||
|
||||
# Database sync endpoint (optional)
|
||||
# VITE_SYNCDB_ENDPOINT=https://your-couchdb-server.com
|
||||
```
|
||||
|
||||
## Firewall (if using iptables)
|
||||
|
||||
```bash
|
||||
# Allow port 3001
|
||||
iptables -A INPUT -p tcp --dport 3001 -j ACCEPT
|
||||
|
||||
# Save rules
|
||||
rc-service iptables save
|
||||
```
|
||||
|
||||
## Reverse Proxy (Optional)
|
||||
|
||||
If using nginx as a reverse proxy:
|
||||
|
||||
```bash
|
||||
apk add nginx
|
||||
```
|
||||
|
||||
Create `/etc/nginx/http.d/immersive.conf`:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name your-domain.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3001;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Enable and start nginx:
|
||||
|
||||
```bash
|
||||
rc-update add nginx default
|
||||
rc-service nginx start
|
||||
```
|
||||
|
||||
## Gitea CI/CD Runner (Optional)
|
||||
|
||||
If using a Gitea Actions runner to deploy, grant the runner user write access to `/opt/immersive`:
|
||||
|
||||
```bash
|
||||
# Add gitea-runner to immersive group
|
||||
adduser gitea-runner immersive
|
||||
|
||||
# Set group write permissions on /opt/immersive
|
||||
chmod -R g+w /opt/immersive
|
||||
|
||||
# Ensure new files inherit group ownership
|
||||
chmod g+s /opt/immersive
|
||||
|
||||
# Allow runner to manage the service
|
||||
# Add to /etc/sudoers.d/gitea-runner:
|
||||
echo 'gitea-runner ALL=(ALL) NOPASSWD: /sbin/rc-service immersive *' > /etc/sudoers.d/gitea-runner
|
||||
echo 'gitea-runner ALL=(ALL) NOPASSWD: /bin/chown -R immersive\:immersive /opt/immersive' >> /etc/sudoers.d/gitea-runner
|
||||
chmod 440 /etc/sudoers.d/gitea-runner
|
||||
```
|
||||
|
||||
The GitHub Actions workflow in `.github/workflows/build.yml` will handle deployment automatically on push to main.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Service fails to start:**
|
||||
```bash
|
||||
# Check logs
|
||||
cat /var/log/immersive/error.log
|
||||
|
||||
# Run manually as immersive user to see errors
|
||||
su -s /bin/sh immersive -c "cd /opt/immersive && NODE_ENV=production node server.js"
|
||||
```
|
||||
|
||||
**Native module errors (leveldown):**
|
||||
```bash
|
||||
# Rebuild native modules
|
||||
cd /opt/immersive
|
||||
npm rebuild leveldown
|
||||
```
|
||||
|
||||
**Permission issues:**
|
||||
```bash
|
||||
# Ensure proper ownership (must be immersive user)
|
||||
chown -R immersive:immersive /opt/immersive
|
||||
chown -R immersive:immersive /var/log/immersive
|
||||
chown -R immersive:immersive /var/run/immersive
|
||||
chmod -R 755 /opt/immersive
|
||||
```
|
||||
|
||||
**Port already in use:**
|
||||
```bash
|
||||
# Find process using port 3001
|
||||
lsof -i :3001
|
||||
# Or
|
||||
netstat -tlnp | grep 3001
|
||||
```
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "immersive",
|
||||
"private": true,
|
||||
"version": "0.0.8-43",
|
||||
"version": "0.0.8-44",
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
|
||||
19
start.sh
Normal file
19
start.sh
Normal file
@ -0,0 +1,19 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Immersive start script for Alpine Linux
|
||||
|
||||
APP_DIR="/opt/immersive"
|
||||
LOG_DIR="/var/log/immersive"
|
||||
PID_FILE="/var/run/immersive.pid"
|
||||
|
||||
cd "$APP_DIR"
|
||||
|
||||
# Environment
|
||||
export NODE_ENV=production
|
||||
export NODE_OPTIONS="--max-old-space-size=2048"
|
||||
|
||||
# Optional: Set port (default 3001)
|
||||
# export PORT=3001
|
||||
|
||||
# Start the server
|
||||
exec node server.js >> "$LOG_DIR/app.log" 2>> "$LOG_DIR/error.log"
|
||||
Loading…
Reference in New Issue
Block a user