Server Setup - RodrigoMattosoSilveira/EnterpriseRemoteSystems GitHub Wiki

1. SSH into the server

From your Mac:

ssh root@YOUR_SERVER_IP # 178.105.46.193

Update the server:

apt update
apt upgrade -y

2. Create a deploy user

adduser deploy
usermod -aG sudo deploy```

Copy your SSH access to the new user:

rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy

Add deploy to the sudo group, before exiting

ssh root@YOUR_SERVER_IP # 178.105.46.193
usermod -aG sudo deploy
groups deploy

Now exit and reconnect, as deploy:

exit
ssh deploy@YOUR_SERVER_IP # 178.105.46.193

3. Install Docker and Compose

If your Hetzner image already has Docker, verify:

docker --version
docker compose version

If not installed:

sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
  sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Allow deploy user to run Docker:

sudo usermod -aG docker deploy
exit
ssh deploy@YOUR_SERVER_IP # 178.105.46.193

Verify:

docker ps
  1. Create app folders
sudo mkdir -p /opt/enterpriseremotesystems/{dev,tst,prd}
sudo mkdir -p /opt/reverse-proxy
sudo chown -R deploy:deploy /opt/enterpriseremotesystems /opt/reverse-proxy

Create per-environment folders:

mkdir -p /opt/enterpriseremotesystems/dev/{data,backups}
mkdir -p /opt/enterpriseremotesystems/tst/{data,backups}
mkdir -p /opt/enterpriseremotesystems/prd/{data,backups}

5. Create reverse proxy with Caddy

Create:

nano /opt/reverse-proxy/docker-compose.yml

Paste:

services:
  caddy:
    image: caddy:2
    container_name: enterpriseremotesystems-caddy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./caddy_data:/data
      - ./caddy_config:/config
    restart: unless-stopped

Create:

nano /opt/reverse-proxy/Caddyfile

For now, use your actual domains:

dev.yourdomain.com { # enterpriseremotesystems.com
  reverse_proxy 127.0.0.1:18081
}

tst.yourdomain.com { # enterpriseremotesystems.com
  reverse_proxy 127.0.0.1:18082
}

app.yourdomain.com { # enterpriseremotesystems.com
  reverse_proxy 127.0.0.1:18083
}

Start Caddy:

cd /opt/reverse-proxy
docker compose up -d

6. Create environment compose files

Create:

$ nano /opt/enterpriseremotesystems/dev/docker-compose.yml

Paste:

services:
  app:
    image: ghcr.io/YOUR_GITHUB_USER/YOUR_REPO:${APP_VERSION}
    container_name: enterpriseremotesystems-${APP_ENV}
    environment:
      APP_ENV: ${APP_ENV}
      HTTP_ADDR: ":8080"
      DB_DRIVER: sqlite
      DB_PATH: /data/app.db
      JWT_SECRET: ${JWT_SECRET}
    volumes:
      - ./data:/data
    ports:
      - "127.0.0.1:${APP_PORT}:8080"
    restart: unless-stopped

Copy to TST/PRD:

cp /opt/enterpriseremotesystems/dev/docker-compose.yml /opt/enterpriseremotesystems/tst/docker-compose.yml
cp /opt/enterpriseremotesystems/dev/docker-compose.yml /opt/enterpriseremotesystems/prd/docker-compose.yml

Create .env files.

/opt/enterpriseremotesystems/dev/.env

APP_ENV=dev
APP_PORT=18081
APP_VERSION=development-latest
JWT_SECRET=replace-with-long-random-dev-secret

/opt/enterpriseremotesystems/tst/.env

APP_ENV=tst
APP_PORT=18082
APP_VERSION=test-latest
JWT_SECRET=replace-with-long-random-tst-secret

/opt/enterpriseremotesystems/prd/.env

APP_ENV=prd
APP_PORT=18083
APP_VERSION=production-latest
JWT_SECRET=replace-with-long-random-prd-secret

Generate secrets with:

openssl rand -base64 48

7. Point DNS to the server

In your DNS provider, add:

A dev.yourdomain.com  YOUR_SERVER_IP
A tst.yourdomain.com  YOUR_SERVER_IP
A app.yourdomain.com  YOUR_SERVER_IP

Once DNS resolves, Caddy will issue HTTPS certificates automatically.

8. Prepare your repo for production image

At repo root, create/update:

Dockerfile

This should build:

frontend → dist
backend → Go server
runtime → /app/server + /app/public

Then your backend must serve the frontend static files after API routes.

In Fiber setup:

routes.Register(app, deps)

app.Static("/", "/app/public")

app.Get("/*", func(c fiber.Ctx) error {
	return c.SendFile("/app/public/index.html")
})

9. Add GitHub secrets

In GitHub repo settings:

Settings → Secrets and variables → Actions → New repository secret

Add:

HETZNER_HOST       = your server IP
HETZNER_USER       = deploy
HETZNER_SSH_KEY    = private key used for deploy access

10. Maintainance

Run these on the Hetzner server.

Stop Caddy reverse proxy

cd /opt/reverse-proxy
docker compose down

Confirm it stopped

docker ps

You should not see enterpriseremotesystems-caddy.

Edit your Caddy domains

nano /opt/reverse-proxy/Caddyfile

Save the updated domains.

Restart Caddy

cd /opt/reverse-proxy
docker compose up -d

Check logs

docker compose logs -f caddy

Caddy should reload and request certificates for the updated domains if DNS is already pointing to your server.

If you want to restart Docker itself

Usually not necessary, but you can do:

sudo systemctl restart docker

Then restart Caddy again:

cd /opt/reverse-proxy
docker compose up -d

Verify

curl -I http://YOUR_DOMAIN
curl -I https://YOUR_DOMAIN

For example:

curl -I https://dev.yourdomain.com