From 9ff65a486848b99ce57eb1134e6ef12efb64e888 Mon Sep 17 00:00:00 2001 From: Artur Havliukovskyi Date: Sun, 19 Oct 2025 21:01:41 +0200 Subject: [PATCH 1/2] Add deployment docker compose to VPS --- .github/workflows/deploy-to-digital-ocean.yml | 32 --------- .github/workflows/on-release-tag.yml | 11 ---- README.md | 28 ++++---- deployment/README.md | 15 +++++ deployment/docker-compose.yaml | 65 +++++++++++++++++++ deployment/nginx.conf | 24 +++++++ docker-compose.yaml | 16 +---- frontend/Dockerfile | 2 +- 8 files changed, 123 insertions(+), 70 deletions(-) delete mode 100644 .github/workflows/deploy-to-digital-ocean.yml create mode 100644 deployment/README.md create mode 100644 deployment/docker-compose.yaml create mode 100644 deployment/nginx.conf diff --git a/.github/workflows/deploy-to-digital-ocean.yml b/.github/workflows/deploy-to-digital-ocean.yml deleted file mode 100644 index 3e4958f..0000000 --- a/.github/workflows/deploy-to-digital-ocean.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Deploy to Digital Ocean -on: - workflow_call: - inputs: - version: - required: true - type: string - workflow_dispatch: - inputs: - version: - description: 'Docker image tag to deploy' - required: true - default: 'latest' - -jobs: - deploy: - # do not execute on forks - if: ${{ github.repository == 'HackYourFuture/CourseHub' }} - runs-on: ubuntu-latest - permissions: - contents: read - environment: coursehub.hyf.dev - concurrency: coursehub.hyf.dev - steps: - - name: Deploy coursehub - uses: digitalocean/app_action/deploy@v2 - env: - IMAGE_TAG_COURSE_HUB_BACKEND: ${{ inputs.version }} - IMAGE_TAG_COURSE_HUB_FRONTEND: ${{ inputs.version }} - with: - app_name: coursehub - token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} diff --git a/.github/workflows/on-release-tag.yml b/.github/workflows/on-release-tag.yml index a3a85e2..349284f 100644 --- a/.github/workflows/on-release-tag.yml +++ b/.github/workflows/on-release-tag.yml @@ -99,14 +99,3 @@ jobs: docker push ghcr.io/hackyourfuture/course-hub-frontend:${{ steps.get-version.outputs.version }} docker push ghcr.io/hackyourfuture/course-hub-frontend:latest fi - - deploy: - # do not execute on forks - if: ${{ github.repository == 'HackYourFuture/CourseHub' && (github.event_name == 'push' || inputs.deploy) }} - needs: [build-course-hub-backend, build-course-hub-frontend] - permissions: - contents: read - uses: ./.github/workflows/deploy-to-digital-ocean.yml - secrets: inherit - with: - version: ${{ needs.build-course-hub-backend.outputs.version }} diff --git a/README.md b/README.md index 515a92a..03348be 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,14 @@ docker compose up -d To run the backend application, you can either: -* From the `backend` directory, run `./gradlew bootRun` in the terminal to us Gradle CLI +* Start application from the terminal: + ```bash + cd backend + ./gradlew bootRun + ``` * Run the `CourseHubApplication` main class from your IDE. -Now you can access the CourseHub frontend UI on `http://localhost:80` and the backend API on `http://localhost:8080`. +Now you can access the CourseHub frontend UI on `http://localhost:3000` and the backend API on `http://localhost:8080`. ### Making requests @@ -55,18 +59,18 @@ You can also see all available endpoints in the [OpenAPI documentation](http://l ### Running the frontend -By default, the frontend will be running on `http://localhost:80` from Docker compose. If you want to run it +By default, the frontend will be running on `http://localhost:3000` from Docker compose. If you want to run it locally, follow the steps below. To install the required dependencies (only once), from the `frontend` directory, run: - -```bash +```bash +cd frontend npm install ``` To run the frontend application locally, from the `frontend` directory, run: - ```bash +cd frontend npm run dev ``` @@ -74,16 +78,16 @@ Now you can access the CourseHub frontend UI on `http://localhost:5173` in devel ### Building docker images -To build a Docker image of the course-hub backend, run the following command: - +To build a Docker image of the course-hub backend, from the `backend` directory, run: ```bash +cd backend ./gradlew bootBuildImage ``` To build a Docker image for the frontend, from the `frontend` directory, run: - ```bash -docker build -t ghcr.io/hackyourfuture/course-hub-frontend frontend +cd frontend +docker build -t ghcr.io/hackyourfuture/course-hub-frontend . ``` #### Running docker image @@ -92,7 +96,7 @@ After the image is built, you can run it using a special Docker Compose profile you're running it from Gradle or IDE)_: ```bash -docker compose --profile include-course-hub up +docker compose up -d ``` ### Cleanup @@ -101,5 +105,5 @@ Keep in mind that containers will keep running in the background even after you the containers, run: ```bash -docker compose --profile include-course-hub down -v +docker compose down -v ``` diff --git a/deployment/README.md b/deployment/README.md new file mode 100644 index 0000000..23d55e4 --- /dev/null +++ b/deployment/README.md @@ -0,0 +1,15 @@ +## Deployment + +This directory contains Docker Compose configuration to deploy everything on a single VPS. + +If the only updates are the image versions, then `watchtower` will take care of updating. Otherwise, first sync the deployment: +```bash +scp -r . user@server:/opt/course-hub/ +``` +then restart docker containers using SSH: +```bash +ssh user@server +cd /opt/course-hub +docker compose down +docker compose up -d +``` diff --git a/deployment/docker-compose.yaml b/deployment/docker-compose.yaml new file mode 100644 index 0000000..d52a412 --- /dev/null +++ b/deployment/docker-compose.yaml @@ -0,0 +1,65 @@ +services: + # Nginx is responsible for global routing + # /api -> course-hub-backend + # / -> course-hub-frontend + nginx: + image: nginx:latest + container_name: nginx + ports: + - "443:443" + - "80:80" + volumes: + - ./nginx.conf:/etc/nginx/conf.d/default.conf + - ./certificates:/etc/nginx/certificates + # Postgres, main data store used by course-hub-backend + postgres: + image: postgres:18 + container_name: postgres + environment: + POSTGRES_USER: course_user + POSTGRES_PASSWORD: course_user_password + POSTGRES_DB: coursehub + volumes: + - postgres:/var/lib/postgresql + ports: + - "5432:5432" + # Redis, auth session storage used by course-hub-backend + redis: + image: redis:7 + container_name: redis + volumes: + - redis:/data + ports: + - "6379:6379" + course-hub-frontend: + image: ghcr.io/hackyourfuture/course-hub-frontend:latest + container_name: course-hub-frontend + environment: + BACKEND_URL: http://localhost/api + ports: + - "3000:3000" + course-hub-backend: + image: ghcr.io/hackyourfuture/course-hub-backend:latest + container_name: course-hub-backend + environment: + JVM_TOOL_OPTS: -XX:ReservedCodeCacheSize=80M + BPL_JVM_THREAD_COUNT: 20 + SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/coursehub + SPRING_DATA_REDIS_HOST: redis + depends_on: + postgres: + condition: service_started + redis: + condition: service_started + ports: + - "8080:8080" + # Agent that monitors whenever new versions of images are published and recreates the containers + watchtower: + image: containrrr/watchtower + volumes: + - /var/run/docker.sock:/var/run/docker.sock + command: --interval 30 + +volumes: + postgres: + redis: diff --git a/deployment/nginx.conf b/deployment/nginx.conf new file mode 100644 index 0000000..b939810 --- /dev/null +++ b/deployment/nginx.conf @@ -0,0 +1,24 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + + location /api { + rewrite ^/api(/.*)$ $1 break; + proxy_pass http://course-hub-backend:8080; + 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; + } + + location / { + proxy_pass http://course-hub-frontend:3000; + 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; + } +} + diff --git a/docker-compose.yaml b/docker-compose.yaml index 1e42cab..002d705 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,6 +1,6 @@ services: postgres: - image: postgres:17 + image: postgres:18 container_name: postgres environment: POSTGRES_USER: course_user @@ -19,17 +19,5 @@ services: environment: BACKEND_URL: http://localhost:8080 ports: - - "80:80" - course-hub-backend: - image: ghcr.io/hackyourfuture/course-hub-backend:latest - container_name: course-hub - profiles: - - include-course-hub - environment: - SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/coursehub - depends_on: - postgres: - condition: service_started - ports: - - "8080:8080" + - "3000:3000" diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 01279e2..77e3e82 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -13,5 +13,5 @@ COPY public/images /usr/share/nginx/html/images COPY nginx.conf /etc/nginx/conf.d/default.conf COPY nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh -EXPOSE 80 +EXPOSE 3000 CMD ["nginx-entrypoint.sh"] From 46b6fa82c7213bdf15f206ae265c3de255835b33 Mon Sep 17 00:00:00 2001 From: Artur Havliukovskyi Date: Sun, 19 Oct 2025 21:05:34 +0200 Subject: [PATCH 2/2] Update deployment/nginx.conf Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- deployment/nginx.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/deployment/nginx.conf b/deployment/nginx.conf index b939810..dda1451 100644 --- a/deployment/nginx.conf +++ b/deployment/nginx.conf @@ -2,7 +2,6 @@ server { listen 80; server_name _; - root /usr/share/nginx/html; location /api { rewrite ^/api(/.*)$ $1 break;