Skip to content

400 Software Development


Setting up home server

Setup static IP/disable Wifi

Download Ubuntu Server Get Ubuntu Server | Download | Ubuntu

Allow laptop to continue running with lid closed:

sudo vi /etc/systemd/logind.conf

Add (remove comment):

HandleLidSwitch=ignore
HandleLidSwitchDocked=ignore

Restart daemon:

sudo systemctl restart systemd-logind.service

find name of lan port:

ip a

edit network interfaces (there is a default in that folder, edit that):

sudo vi /etc/netplan/50-cloud-init.yaml

add:

network:
  version: 2
  renderer: networkd
  ethernets:
    enp2s0f0:
      addresses: [192.168.1.50/24]
      routes:
        - to: default
          via: 192.168.1.1
      nameservers:
        addresses: [1.1.1.1, 8.8.8.8]

apply policy:

sudo netplan generate
sudo netplan apply

Install docker + home assisstant

Make service folder structure:

sudo mkdir -p /srv/homeassistant/{compose,config,data,logs,secrets}
sudo chown -R $USER:$USER /srv/homeassistant

Create docker compose:

sudo vi /srv/homeassistant/compose/docker-compose.yml

Paste:

services:
  homeassistant:
    image: ghcr.io/home-assistant/home-assistant:stable
    container_name: homeassistant
    restart: unless-stopped
    network_mode: host            # Needed for mDNS/SSDP/Bluetooth
    privileged: true
    volumes:
      - ../config:/config
      - ../data/ha:/data
      - /etc/localtime:/etc/localtime:ro
      - /run/dbus:/run/dbus:ro
      # Example USB adapter (Zigbee/Z-Wave/RCP):
      # - /dev/serial/by-id/...:/dev/ttyUSB0
    environment:
      - TZ=Africa/Johannesburg

  matter-server:
    image: ghcr.io/home-assistant-libs/python-matter-server:stable
    container_name: matter-server
    restart: unless-stopped
    network_mode: host
    volumes:
      - ../data/matter:/data
    environment:
      - MATTER_SERVER_STORAGE=/data
      - TZ=Africa/Johannesburg

Configure HA conf and add following (only accessible on local host)

sudo vi /srv/homeassistant/config/configuration.yaml
http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 127.0.0.1      # host nginx on the same box
    - ::1            # IPv6 loopback (optional but harmless)

Create nginx server to forward to HA over port 80:

sudo vi /etc/nginx/sites-available/homeassistant.conf
server {
    listen 80;
    server_name ha.example.com;

    location / {
        proxy_pass http://127.0.0.1:8123;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
    }
}

Enable + certbot:

sudo ln -s /etc/nginx/sites-available/homeassistant.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d home.cliftonbartholomew.co.za

Start homeassistant:

cd /srv/homeassistant/compose
sudo docker compose up -d

Install openproject

Make service folder:

sudo mkdir -p /srv/openproject/{compose,config,data,logs,secrets}
sudo chown -R $USER:$USER /srv/openproject
 ```

Create .env:
```bash
sudo vi /srv/openproject/compose/.env

Paste:

# ---- App ----
APP_DOMAIN=projects.cliftonbartholomew.co.za
APP_PORT=8085                     # host loopback port for nginx to hit

# ---- Postgres ----
POSTGRES_DB=openproject
POSTGRES_USER=openproject
POSTGRES_PASSWORD=aR31DovcdmvSml96Rh1g9t5KGMAFXCIGi0DYw8MwCgolf
POSTGRES_VERSION=17

# ---- Email (optional: set when you wire SMTP) ----
# EMAIL__DELIVERY__METHOD=smtp
# EMAIL__SMTP__ADDRESS=smtp.yourhost.com
# EMAIL__SMTP__PORT=587
# EMAIL__SMTP__DOMAIN=example.com
# EMAIL__SMTP__AUTHENTICATION=login
# EMAIL__SMTP__ENABLE__STARTTLS__AUTO=true
# EMAIL__SMTP__USER__NAME=postmaster@example.com
# EMAIL__SMTP__PASSWORD=yourpassword

Create docker compose:

sudo vi /srv/openproject/compose/docker-compose.yml

Paste:

networks:
  web:
    external: true   # shared reverse-proxy network (create it if you don’t already have it)
  default:
    name: openproject_internal

services:
  postgres:
    image: postgres:${POSTGRES_VERSION}
    container_name: openproject_postgres
    restart: unless-stopped
    env_file:
      - .env
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - ../data/pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 10

  memcached:
    image: memcached:1.6-alpine
    container_name: openproject_memcached
    restart: unless-stopped
    command: ["memcached", "-m", "128", "-I", "5m"]  # 128MB cache, 5MB max item size
    healthcheck:
      test: ["CMD", "sh", "-c", "printf 'stats\n' | nc 127.0.0.1 11211 | grep -q 'STAT'"]
      interval: 30s
      timeout: 5s
      retries: 5

  openproject:
    image: openproject/openproject:16.4.0
    container_name: openproject_app
    depends_on:
      postgres:
        condition: service_healthy
      memcached:
        condition: service_started
    restart: unless-stopped
    env_file:
      - .env
    environment:
      # --- Core connectivity ---
      DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
      OPENPROJECT_HOST__NAME: ${APP_DOMAIN}
      OPENPROJECT_HTTPS: "true"          # TLS terminated at host nginx
      OPENPROJECT_PORT: "80"

      # --- Cache (memcached) ---
      CACHE__STORE__MEMCACHE__SERVERS: "memcached:11211"

      # --- Email (uncomment if set in .env) ---
      # EMAIL__DELIVERY__METHOD: ${EMAIL__DELIVERY__METHOD}
      # EMAIL__SMTP__ADDRESS: ${EMAIL__SMTP__ADDRESS}
      # EMAIL__SMTP__PORT: ${EMAIL__SMTP__PORT}
      # EMAIL__SMTP__DOMAIN: ${EMAIL__SMTP__DOMAIN}
      # EMAIL__SMTP__AUTHENTICATION: ${EMAIL__SMTP__AUTHENTICATION}
      # EMAIL__SMTP__ENABLE__STARTTLS__AUTO: ${EMAIL__SMTP__ENABLE__STARTTLS__AUTO}
      # EMAIL__SMTP__USER__NAME: ${EMAIL__SMTP__USER__NAME}
      # EMAIL__SMTP__PASSWORD: ${EMAIL__SMTP__PASSWORD}

      # --- Optional: force locale/timezone ---
      # OPENPROJECT_DEFAULT__LOCALE: en_ZA
      # OPENPROJECT_DEFAULT__TIMEZONE: Africa/Johannesburg

    volumes:
      - ../data/assets:/var/openproject/assets        # attachments, repos, etc.
      - ../logs/openproject:/var/log/supervisor       # container logs (optional)
    ports:
      - "127.0.0.1:${APP_PORT}:80"
    networks:
      - default
      - web

Create docker network:

sudo docker network create web || true

Create nginx conf:

sudo vi /etc/nginx/sites-available/openproject.conf

Paste:

server {
    server_name projects.cliftonbartholomew.co.za

    client_max_body_size 100m;

    location / {
        proxy_pass http://127.0.0.1:8085;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;

        # WebSocket support (simplified)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Link + certbot:

sudo ln -s /etc/nginx/sites-available/openproject.conf /etc/nginx/sites-enabled/openproject.conf
sudo nginx -t && sudo systemctl reload nginx

# Let’s Encrypt (will convert to HTTPS config automatically)
sudo certbot --nginx -d projects.cliftonbartholomew.co.za

Start openproject:

cd /srv/openproject/compose
sudo docker compose up -d

See also