| # VPS Deployment Guide |
|
|
| Deploy OSW Studio on a VPS with full security hardening. Tested on Hetzner Cloud, but applies to any Ubuntu/Debian VPS. |
|
|
| --- |
|
|
| ## Pre-Creation (Hetzner Console) |
|
|
| > Skip this section if using a different VPS provider. The server setup steps below work on any Ubuntu 22.04+ server. |
|
|
| ### 1. Add SSH Key |
|
|
| Security β SSH Keys β Add SSH Key |
|
|
| If you don't have one locally, generate it first: |
|
|
| ```bash |
| ssh-keygen -t ed25519 -C "osws-server" |
| cat ~/.ssh/id_ed25519.pub |
| ``` |
|
|
| Paste the public key into Hetzner. |
|
|
| ### 2. Create Cloud Firewall |
|
|
| Firewalls β Create Firewall |
|
|
| **Inbound rules (TCP only):** |
|
|
| | Port | Protocol | Source | |
| |------|----------|--------| |
| | 22 | TCP | Any | |
| | 80 | TCP | Any | |
| | 443 | TCP | Any | |
|
|
| Do NOT add port 3000 or any other app ports. |
|
|
| ### 3. Create Server |
|
|
| - Select your SSH key |
| - Attach the firewall |
| - Smallest instance (CX11/CX21) is fine for testing |
|
|
| --- |
|
|
| ## Server Setup |
|
|
| SSH into the server: |
|
|
| ```bash |
| ssh root@<your-ip> |
| ``` |
|
|
| ### Update System |
|
|
| ```bash |
| apt update && apt upgrade -y |
| ``` |
|
|
| ### Create Non-Root User |
|
|
| ```bash |
| adduser osws --disabled-password --gecos "" |
| mkdir -p /home/osws/.ssh |
| cp ~/.ssh/authorized_keys /home/osws/.ssh/ |
| chown -R osws:osws /home/osws/.ssh |
| chmod 700 /home/osws/.ssh |
| chmod 600 /home/osws/.ssh/authorized_keys |
| ``` |
|
|
| ### Harden SSH |
|
|
| ```bash |
| sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config |
| sed -i 's/PermitRootLogin yes/PermitRootLogin prohibit-password/' /etc/ssh/sshd_config |
| systemctl restart ssh |
| ``` |
|
|
| ### Install fail2ban |
|
|
| ```bash |
| apt install fail2ban -y |
| systemctl enable fail2ban --now |
| ``` |
|
|
| ### Add Swap (Prevents OOM on Small Instances) |
|
|
| ```bash |
| fallocate -l 2G /swapfile |
| chmod 600 /swapfile |
| mkswap /swapfile |
| swapon /swapfile |
| echo '/swapfile none swap sw 0 0' >> /etc/fstab |
| ``` |
|
|
| ### Install & Configure Nginx |
|
|
| ```bash |
| apt install nginx -y |
| |
| cat > /etc/nginx/sites-available/osws << 'EOF' |
| server { |
| listen 80 default_server; |
| server_name _; |
| |
| location / { |
| proxy_pass http://127.0.0.1:3000; |
| 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_cache_bypass $http_upgrade; |
| } |
| } |
| EOF |
| |
| ln -s /etc/nginx/sites-available/osws /etc/nginx/sites-enabled/ |
| rm /etc/nginx/sites-enabled/default |
| nginx -t && systemctl reload nginx |
| ``` |
|
|
| --- |
|
|
| ## App Deployment |
|
|
| ### Switch to osws User |
|
|
| ```bash |
| su - osws |
| ``` |
|
|
| ### Install Node.js |
|
|
| ```bash |
| curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash |
| source ~/.bashrc |
| nvm install 20 |
| ``` |
|
|
| ### Clone Repository |
|
|
| ```bash |
| git clone https://github.com/o-stahl/osw-studio.git |
| cd osw-studio |
| ``` |
|
|
| ### Create Environment File |
|
|
| ```bash |
| SESSION_SECRET=$(openssl rand -base64 32) |
| ANALYTICS_SECRET=$(openssl rand -base64 32) |
| SECRETS_ENCRYPTION_KEY=$(openssl rand -base64 32) |
| |
| cat > .env << EOF |
| NEXT_PUBLIC_SERVER_MODE=true |
| SESSION_SECRET=$SESSION_SECRET |
| SECURE_COOKIES=false |
| ANALYTICS_SECRET=$ANALYTICS_SECRET |
| SECRETS_ENCRYPTION_KEY=$SECRETS_ENCRYPTION_KEY |
| EOF |
| ``` |
|
|
| > **Note:** `SECURE_COOKIES=false` is required when running HTTP without SSL. Remove this line after adding HTTPS. |
| > |
| > No `ADMIN_PASSWORD` needed. On first visit to `/admin`, you'll create the admin account interactively. |
|
|
| ### Build & Start |
|
|
| ```bash |
| npm install |
| npm run build |
| npm install -g pm2 |
| pm2 start npm --name "osws" -- start |
| pm2 save |
| ``` |
|
|
| ### Enable PM2 on Boot |
|
|
| Exit back to root (`exit`) and run: |
|
|
| ```bash |
| env PATH=$PATH:/home/osws/.nvm/versions/node/v20.18.1/bin pm2 startup systemd -u osws --hp /home/osws |
| ``` |
|
|
| > **Note:** Adjust the Node version path if you installed a different version. |
|
|
| --- |
|
|
| ## Access |
|
|
| - **Studio:** `http://<your-ip>/` |
| - **Admin:** `http://<your-ip>/admin` |
|
|
| --- |
|
|
| ## Updating OSW Studio |
|
|
| ```bash |
| su - osws |
| cd ~/osw-studio |
| git pull |
| npm install |
| npm run build |
| pm2 restart osws |
| ``` |
|
|
| > **Important:** Any changes to `NEXT_PUBLIC_*` environment variables require a rebuild (`npm run build`) β these are baked in at compile time. |
| |
| --- |
| |
| ## Adding HTTPS (Recommended) |
| |
| Once you have a domain pointing to your server: |
| |
| As root: |
| |
| ```bash |
| apt install certbot python3-certbot-nginx -y |
| certbot --nginx -d your-domain.com |
| ``` |
| |
| Then update the environment and rebuild: |
| |
| ```bash |
| su - osws |
| cd ~/osw-studio |
| |
| # Edit .env to remove SECURE_COOKIES=false |
| nano .env |
| |
| npm run build |
| pm2 restart osws |
| ``` |
| |
| --- |
| |
| ## Security Checklist |
| |
| - β
Never expose port 3000 directly β always use nginx as reverse proxy |
| - β
App runs as non-root user `osws` |
| - β
SSH is key-only, no password authentication |
| - β
Cloud firewall blocks all ports except 22, 80, 443 |
| - β
fail2ban protects against brute force attempts |
| - β
Swap prevents out-of-memory crashes on small instances |
| |
| --- |
| |
| ## Next Steps |
| |
| - **[Server Mode](?doc=server-mode)** - Full Server Mode documentation |
| - **[Deployment Publishing](?doc=site-publishing)** - Publish deployments with analytics and SEO |
| - **[Backend](?doc=backend-features)** - Database, edge functions, secrets |
| |