commit 249bed66e258b20c1ee32013a27c713fe3bff8dc Author: Raghav <1858838+quantumrag@users.noreply.github.com> Date: Fri Apr 17 08:16:15 2026 +0530 Initial Nextcloud Docker setup for nxt.bhatfamily.in diff --git a/.env b/.env new file mode 100644 index 0000000..28097cd --- /dev/null +++ b/.env @@ -0,0 +1,10 @@ +# .env +# Database +NEXTCLOUD_DB_NAME=nextcloud +NEXTCLOUD_DB_USER=nextcloud +NEXTCLOUD_DB_PASSWORD=u@ZFN8y8WzwVPgE +NEXTCLOUD_DB_ROOT_PASSWORD=FNEyZG6pzPv*uf9 + +# Nextcloud admin +NEXTCLOUD_ADMIN_USER=admin +NEXTCLOUD_ADMIN_PASSWORD=7VZxFLJd-8Lb*pp diff --git a/README.md b/README.md new file mode 100644 index 0000000..a196f6f --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# Nextcloud on Ubuntu via Docker for nxt.bhatfamily.in + +This repo deploys Nextcloud using Docker on Ubuntu, fronted by Nginx on: + +- HTTP: 8082 +- HTTPS: 8446 + +It is intended to serve the hostname **nxt.bhatfamily.in**. + +## Prerequisites + +- Ubuntu host with Docker and Docker Compose installed +- Static public IP (or Cloudflare Tunnel) +- Domain `bhatfamily.in` managed in Cloudflare +- Basic familiarity with UFW and router port forwarding + +## DNS (Cloudflare) + +1. In Cloudflare DNS for `bhatfamily.in`, create: + + - Type: A + - Name: `nxt` + - IPv4: your Ubuntu server public IP + - Proxy: DNS only (grey cloud) + +2. Forward ports 8082 and 8446 from your router to the Ubuntu host. + +## First-time setup + +1. Clone this repo and enter directory: + + ```bash + git clone nextcloud-docker-nxt.bhatfamily.in + cd nextcloud-docker-nxt.bhatfamily.in diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..22fa304 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,72 @@ +# docker-compose.yml +version: "3.8" + +services: + db: + image: mariadb:11 + container_name: nextcloud-db + restart: unless-stopped + command: [ + "mariadbd", + "--transaction-isolation=READ-COMMITTED", + "--binlog-format=ROW", + "--innodb_read_only_compressed=OFF" + ] + env_file: + - .env + environment: + - MYSQL_DATABASE=${NEXTCLOUD_DB_NAME} + - MYSQL_USER=${NEXTCLOUD_DB_USER} + - MYSQL_PASSWORD=${NEXTCLOUD_DB_PASSWORD} + - MYSQL_ROOT_PASSWORD=${NEXTCLOUD_DB_ROOT_PASSWORD} + volumes: + - db_data:/var/lib/mysql + networks: + - nextcloud-net + + app: + image: nextcloud:29-apache + container_name: nextcloud-app + restart: unless-stopped + env_file: + - .env + environment: + - MYSQL_HOST=db + - MYSQL_DATABASE=${NEXTCLOUD_DB_NAME} + - MYSQL_USER=${NEXTCLOUD_DB_USER} + - MYSQL_PASSWORD=${NEXTCLOUD_DB_PASSWORD} + - NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN_USER} + - NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD} + - NEXTCLOUD_TRUSTED_DOMAINS=nxt.bhatfamily.in + - NEXTCLOUD_OVERWRITEHOST=nxt.bhatfamily.in:8446 + - NEXTCLOUD_OVERWRITEPROTOCOL=https + depends_on: + - db + volumes: + - nextcloud_data:/var/www/html + networks: + - nextcloud-net + + web: + image: nginx:1.25-alpine + container_name: nextcloud-web + restart: unless-stopped + ports: + - "8082:80" + - "8446:443" + volumes: + - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro + - ./nginx/ssl:/etc/nginx/ssl:ro + - nextcloud_data:/var/www/html:ro + depends_on: + - app + networks: + - nextcloud-net + +volumes: + db_data: + nextcloud_data: + +networks: + nextcloud-net: + driver: bridge diff --git a/nginx/nginx.conf b/nginx/nginx.conf new file mode 100644 index 0000000..6eb2015 --- /dev/null +++ b/nginx/nginx.conf @@ -0,0 +1,39 @@ +# nginx/nginx.conf +server { + listen 80; + server_name nxt.bhatfamily.in; + + # Redirect all HTTP to HTTPS on 8446 + return 301 https://$host:8446$request_uri; +} + +server { + listen 443 ssl; + http2 on; + server_name nxt.bhatfamily.in; + + ssl_certificate /etc/nginx/ssl/nxt.bhatfamily.in.crt; + ssl_certificate_key /etc/nginx/ssl/nxt.bhatfamily.in.key; + ssl_dhparam /etc/nginx/ssl/dhparam.pem; + + # Good basic TLS settings (tune further as needed) + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + ssl_ciphers HIGH:!aNULL:!MD5; + + client_max_body_size 10240M; + add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload" always; + + location / { + proxy_pass http://nextcloud-app:80; + proxy_http_version 1.1; + 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 https; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port 8446; + proxy_read_timeout 3600; + proxy_send_timeout 3600; + } +} diff --git a/nginx/ssl/dhparam.pem b/nginx/ssl/dhparam.pem new file mode 100644 index 0000000..167ef3b --- /dev/null +++ b/nginx/ssl/dhparam.pem @@ -0,0 +1,8 @@ +-----BEGIN DH PARAMETERS----- +MIIBCAKCAQEA6URKKHp177eP2+UP//DCEmAP63DpZNtVQeC0PO3x14Jomg6bicVa +ySBkEGtSHw2WMtcGe/4P0AyrPuVL8VgIUvFXzLO2j8XeHZ/Gu5LWZw3bAwONBUC2 +N4l2msyNMRoPBnu/BjjlYEkDEsqo5VX38ZdaCSa7ZgseYAWV524jW2VewB58ox6s +c0iw5aoq1XrJp5+mgj/XG2i0xO5DvdLfDa/Yvslhi8MT7GLvfoHziLcybB949ZK4 +PSCPvvZE17P5oQhRgLhCWe2VW7TchRLy4FTk8Iv2HV7ndDzZEsXIGjy8MgyDQNCd +YxIIa1Fkw90Bzw8htyNX3AcakTlWy8d/ZwIBAg== +-----END DH PARAMETERS----- diff --git a/nginx/ssl/nxt.bhatfamily.in.crt b/nginx/ssl/nxt.bhatfamily.in.crt new file mode 100644 index 0000000..c85bf6d --- /dev/null +++ b/nginx/ssl/nxt.bhatfamily.in.crt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFGTCCAwGgAwIBAgIUHAA348XpRrN8g/F5MM2n1PRwGOQwDQYJKoZIhvcNAQEL +BQAwHDEaMBgGA1UEAwwRbnh0LmJoYXRmYW1pbHkuaW4wHhcNMjYwNDE3MDIzMDU4 +WhcNMjcwNDE3MDIzMDU4WjAcMRowGAYDVQQDDBFueHQuYmhhdGZhbWlseS5pbjCC +AiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKygVD9ln901VJIhiq7cDu5Q +mxvCN+jlfMzSDeIri7SDqBKFgLh5l+c0SddNpKmRqnSLWAFPrJbKRXuwmE5sAD6t +wPDKiwxpX09IP6lhBojjKAPRpHNCWw0JRJJgOYixm7/dsDIenTK55AuQkIeM9Iic +R6ohzoeJ+zDF+etGedh0O+UI7XDhZJECUOGUJBxSPepeETww436FNJfoLx7VxS+4 +VF65Zq7uqv18FhqO1gab4q/ydwZ9AbIILfUdZXWbd6z3DbmKug0FPyFOASwfNCFD +mJ7dIFBsOu6mhRpV881Gjitn3O5RwrIC0QJ4R2FQ/SKhat1GUlmNNx9ELt+VH6VB ++jyNk9/e3pEHKlweKki6HW8tB/MT7ZZkk0/qD0Kg/SEqLsxblmnLv5s6GXLPfMNR +yqV7Gs0ZlXbCkFSYrMSh5A4NACJUtGpq5rTZNKtn9taMHGuO3l0h8K88Su0t2nRM +yxBgL6WizvdsQvqHowzjQVnBds6cTUwR7QLxUUbKr4uuZoo0lleUlHGvubNDdOIE +6NKuJBB1rcENO5B6tITPRrOYrurDwVPVvHBfLlYHEQvvaypWQtDpcf39GTHCIekK +C4NysXrDGbshcK9fH30VwiR78kkvXLShbytGL4aq6yf9Gp77iMSt69GUCR4wIGMu +ck7H96LmA82vx8Q+bZhDAgMBAAGjUzBRMB0GA1UdDgQWBBTV67Dyn+iaubohmUDq +OBp78vgKJTAfBgNVHSMEGDAWgBTV67Dyn+iaubohmUDqOBp78vgKJTAPBgNVHRMB +Af8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQA4t0pSJViW66Ue+KX5YD3GWW5Z +RQy8XwrPqHqFHU8yjf5jeK2bsBXQC5Ovr/dQnlLs2GdGvHFmthcWFlpzF4zRzVFK +jy2ZgqDYV+ET0rPqpGkz3iThTRLWAEm7q6gwZtmGQSdpuKK5Ei47TqfOtq/r0Juy +cg12PQs3wUuavNXWVz/uRis+RZiWZhn9+xz6XRhEPZjTOyIoS65frm1uvFv96Sw+ +0Lrxn2e/oFRuV0yrnaIV/bJjPvf0/VGVrQ0DmWfrJ1ywXwl+Ys/Ib9LmVTU0tdlL +vVR5a/6Lg5iSNiQqm8VrLspFj5iv6x65ygb8klbUxHz7zPmwGRrG8UxscEg9pF5u +TaZfCk+V6VSp+VjdLUUdmn4UGSN2tPMBdIwQjZAsBHN6pPaTE9EpxOqBQG64rUzt +jR8damZ3X+qHaS8Lz3wvRFYr1NNVC2oTx5+1neM0zfSEcGkmNlkONcmkWghzggI/ +g7xTm3oSlrUKqw+cSoksMaa8bCuXMR0CtM8x/sPZgwiDwQVQOGBVQKmnMbMq9eGh +QkGHAERUeGUNArdYeYxzjFWQ1EmWIy9kjmVIePt4qNpGQWQ/lk2XTkScsG4GrQ1q +JVqFP1RwTkZfgo1KPTT1FrxVawuIiSemaqmTw8y08zKYw3UJxwVhgo50cmiSiCh7 +UunNLvfJ2QgFnV0yYw== +-----END CERTIFICATE----- diff --git a/nginx/ssl/nxt.bhatfamily.in.key b/nginx/ssl/nxt.bhatfamily.in.key new file mode 100644 index 0000000..9b7e2e5 --- /dev/null +++ b/nginx/ssl/nxt.bhatfamily.in.key @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +6RSKsmjGRIZba+3ia3R8g8Z2rwhv +-----END PRIVATE KEY----- diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..7a35383 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# scripts/install.sh +set -euo pipefail + +REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SSL_DIR="${REPO_DIR}/nginx/ssl" +DOMAIN="nxt.bhatfamily.in" + +echo "==> Ensuring required packages are installed (docker, docker-compose, ufw, openssl)..." +if ! command -v docker >/dev/null 2>&1; then + echo "Docker is not installed. Install Docker and rerun this script." + exit 1 +fi + +if ! command -v docker compose >/dev/null 2>&1 && ! command -v docker-compose >/dev/null 2>&1; then + echo "docker compose / docker-compose is not installed. Install Docker Compose and rerun." + exit 1 +fi + +if ! command -v ufw >/dev/null 2>&1; then + echo "ufw not found. Installing ufw requires root and internet access." +fi + +mkdir -p "${SSL_DIR}" + +echo "==> Generating self-signed TLS certificate for ${DOMAIN} (valid 365 days)..." +if [ ! -f "${SSL_DIR}/${DOMAIN}.crt" ] || [ ! -f "${SSL_DIR}/${DOMAIN}.key" ]; then + openssl req -x509 -nodes -newkey rsa:4096 \ + -keyout "${SSL_DIR}/${DOMAIN}.key" \ + -out "${SSL_DIR}/${DOMAIN}.crt" \ + -days 365 \ + -subj "/CN=${DOMAIN}" +else + echo "Certificate already exists, skipping generation." +fi + +if [ ! -f "${SSL_DIR}/dhparam.pem" ]; then + echo "==> Generating dhparam (this may take a while)..." + openssl dhparam -out "${SSL_DIR}/dhparam.pem" 2048 +fi + +echo "==> Configuring UFW firewall rules (allow 8082/tcp and 8446/tcp)..." +if command -v ufw >/dev/null 2>&1; then + sudo ufw allow 8082/tcp comment "Nextcloud HTTP" + sudo ufw allow 8446/tcp comment "Nextcloud HTTPS" +else + echo "ufw not installed; ensure ports 8082 and 8446 are open in your firewall/router." +fi + +echo "==> Starting Nextcloud stack via Docker Compose..." +cd "${REPO_DIR}" + +if command -v docker compose >/dev/null 2>&1; then + docker compose pull + docker compose up -d +else + docker-compose pull + docker-compose up -d +fi + +echo "==> Nextcloud should now be reachable at:" +echo " http://${DOMAIN}:8082 (redirects to HTTPS)" +echo " https://${DOMAIN}:8446" +echo "" +echo "NOTE: Browser will warn about self-signed certificate. Replace with a valid cert for production." diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..e8a18ea --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +# scripts/test.sh +set -euo pipefail + +DOMAIN="nxt.bhatfamily.in" +HTTP_PORT=8082 +HTTPS_PORT=8446 + +echo "==> Testing HTTP redirect..." +curl -I "http://${DOMAIN}:${HTTP_PORT}" || { + echo "HTTP test failed." + exit 1 +} + +echo "==> Testing HTTPS endpoint (ignoring self-signed cert errors)..." +curl -k -I "https://${DOMAIN}:${HTTPS_PORT}" || { + echo "HTTPS test failed." + exit 1 +} + +echo "==> Quick application-level check (Nextcloud status.php)..." +curl -k "https://${DOMAIN}:${HTTPS_PORT}/status.php" || { + echo "status.php test failed." + exit 1 +} + +echo "All tests passed." diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh new file mode 100755 index 0000000..1a5ba4f --- /dev/null +++ b/scripts/uninstall.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# scripts/uninstall.sh +set -euo pipefail + +REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +echo "==> Stopping and removing Docker containers/volumes..." + +cd "${REPO_DIR}" + +if command -v docker compose >/dev/null 2>&1; then + docker compose down -v +else + docker-compose down -v +fi + +echo "==> (Optional) Remove persistent data volumes manually if desired:" +echo " docker volume ls | grep nextcloud" +echo " docker volume rm # if you want to fully wipe data" + +echo "Uninstall complete."