So, somehow I found this app called Reactive Resume, which is a resume builder on Portainer and I thought, might as well give it a try. However, the version on Portainer is outdated so I decided to dig deeper and found a proper way to deploy this Resume builder app, which I’ll show you in this post.

Guide to self-host Reactive Resume

You can directly go to https://rxresu.me/ to use it. However since this is a self-hosted guide and I like torturing myself, I’ll be setting up Docker on a Debian Virtual Machine running in Proxmox VE. And of course, choose whichever method you prefer to deploy/host it.

Disclaimer

There’s two versions to know of: v3.8.4 and the newest version v4.x.x. Here’s the differences:

  • v3.8.4:
    • Still has markdown support (used in Notion, Github, obsidian)
    • Less templates
  • v4.x.x:
    • Changed from markdown > rich text editor
    • More templates, more features
    • Their official website https://rxresu.me/ uses the latest version

I said in the title that this will be about installing V4 but well… might as well include both versions in case you’ll need it. But for now, the steps to install Docker is the same.

Install Docker

If you don’t have a Linux virtual machine, I recommend checking out this guide on how to virtualize Debian Server with Proxmox VE or whichever method you can use to get a Linux machine or VM up and working.

Access your computer terminal and SSH to your Linux machine via command:

1
2
3
ssh [username]@[IP_address]
#Example
ssh [email protected]

Use this simple script to install Docker:

1
curl -sSL https://get.docker.com | sh

Install Reactive Resume V4

Good ol’ Terminal way

Check your Linux machine’s IP address using ip a or access your router settings.

Then create a folder named reactive-resume-v4 and make a docker-compose.yml file:

1
2
mkdir reactive-resume-v4
touch docker-compose.yml

Now run nano docker-compose.yml and paste the below docker compose code in:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
version: "3.8"

# In this Docker Compose example, it assumes that you maintain a reverse proxy externally (or chose not to).
# The only two exposed ports here are from minio (:9000) and the app itself (:80).
# If these ports are changed, ensure that the env vars passed to the app are also changed accordingly.

services:
  # Database (Postgres)
  postgres:
    image: postgres:15-alpine
    restart: unless-stopped
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: postgres
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  # Storage (for image uploads)
  minio:
    image: minio/minio
    restart: unless-stopped
    command: server /data
    ports:
      - 9000:9000
    volumes:
      - minio_data:/data
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin

  # Chrome Browser (for printing and previews)
  chrome:
    image: browserless/chrome:1.61.0-puppeteer-21.4.1
    restart: unless-stopped
    environment:
      TOKEN: chrome_token
      EXIT_ON_HEALTH_FAILURE: true
      PRE_REQUEST_HEALTH_CHECK: true

  # Redis (for cache & server session management)
  redis:
    image: redis:alpine
    restart: unless-stopped
    command: redis-server --requirepass password

  app:
    image: amruthpillai/reactive-resume:latest
    restart: unless-stopped
    ports:
      - 80:80
    depends_on:
      - postgres
      - minio
      - redis
      - chrome
    environment:
      # -- Environment Variables --
      PORT: 80
      NODE_ENV: production

      # -- URLs --
      PUBLIC_URL: http://${IP_ADDRESS}
      STORAGE_URL: http://${IP_ADDRESS}:9000/default

      # -- Printer (Chrome) --
      CHROME_TOKEN: chrome_token
      CHROME_URL: ws://chrome:3000

      # -- Database (Postgres) --
      DATABASE_URL: postgresql://postgres:postgres@postgres:5432/postgres
      # explain this url postgresql://postgres:postgres@postgres:5432/postgres  postgresql://username:password@host:port/database

      # -- Auth --
      ACCESS_TOKEN_SECRET: access_token_secret
      REFRESH_TOKEN_SECRET: refresh_token_secret

      # -- Emails --
      MAIL_FROM: noreply@localhost
      # SMTP_URL: smtp://user:pass@smtp:587 # Optional

      # -- Storage (Minio) --
      STORAGE_ENDPOINT: minio
      STORAGE_PORT: 9000
      STORAGE_REGION: us-east-1 # Optional
      STORAGE_BUCKET: default
      STORAGE_ACCESS_KEY: minioadmin
      STORAGE_SECRET_KEY: minioadmin
      STORAGE_USE_SSL: false

      # -- Cache (Redis) --
      REDIS_URL: redis://default:password@redis:6379

      GITHUB_CLIENT_ID: github_client_id
      GITHUB_CLIENT_SECRET: github_client_secret
      GITHUB_CALLBACK_URL: http://${IP_ADDRESS}:3000/api/auth/github/callback

      # -- Google (Optional) --
      GOOGLE_CLIENT_ID: google_client_id
      GOOGLE_CLIENT_SECRET: google_client_secret
      GOOGLE_CALLBACK_URL: http://${IP_ADDRESS}:3000/api/auth/google/callback

volumes:
  minio_data:
  postgres_data:

Exit by pressing Ctrl+X > Y > Enter.

Within the folder, create another .env file by running nano .env

1
IP_ADDRESS=10.0.0.22

Change the address to your actual Linux VM IP address.

Now run docker compose up:

1
sudo docker compose up -d

Now you should be able to access the web interface of Reactive Resume via your IP address! For example in my case, it’s http://10.0.0.22/.

Reactive Resume webpage

Congratualtions! You’re now self hosting your own Resume builder app.

The Portainer way

Install Portainer using the below script:

1
2
3
sudo docker volume create portainer_data
sudo docker run -d -p 8000:8000 -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce:latest
sudo docker ps

Then go to the IP address of your machine with port :9443 using HTTPS and start setting up Portainer. Also, the browser will try warning you but just bypass it. This should come up, now you can start setting up Portainer.

Portainer Setup

After that, choose getting started. Then go to the local environment and on the left side bar, choose Stacks. Click on Add Stack, then copy paste the Reactive Resume V4 docker compose code from above.

For the environment variables, go below, click on Add an environment variable. Name is IP_ADDRESS and the value is your IP address, in my case 10.0.0.22.

Oh yeah, remember to fill in the stack name too, I always forget to do that xD.

Now click on deploy, wait a bit and now you can access the web interface by entering the machine’s IP address in your browser. Again, in my case, 10.0.0.22.

Install Reactive Resume V3.8.4

The process is almost identical to V4, except the docker compose file is different.

docker-compose.yml:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# Taken from https://noted.lol/reactive-resume with some changes in values for easy value editing through .env for Docker Compose or stack.env for Portainer Stack deploy

# You can also just edit the values directly

version: "3.8"

services:
  postgres:
    image: postgres:alpine
    restart: always
    ports:
      - 5432:5432
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      start_period: 15s
      interval: 30s
      timeout: 30s
      retries: 3
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}

  server:
    image: amruthpillai/reactive-resume:server-latest
    restart: always
    ports:
      - 3100:3100
    depends_on:
      - postgres
    environment:
      - PUBLIC_URL=http://${IP_ADDRESS}:3000
      - PUBLIC_SERVER_URL=http://${IP_ADDRESS}:3100
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - SECRET_KEY=${SECRET_KEY}
      - POSTGRES_HOST=postgres
      - POSTGRES_PORT=5432
      - JWT_SECRET=${JWT_SECRET}
      - JWT_EXPIRY_TIME=604800
      
  client:
    image: amruthpillai/reactive-resume:client-latest
    restart: always
    ports:
      - 3000:3000
    depends_on:
      - server
    environment:
      - PUBLIC_URL=http://${IP_ADDRESS}:3000
      - PUBLIC_SERVER_URL=http://${IP_ADDRESS}:3100
      
volumes:
  pgdata:

.env:

1
2
3
4
5
6
IP_ADDRESS=0.0.0.22
POSTGRES_DB=postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
SECRET_KEY=change-me-to-something-secure
JWT_SECRET=change-me-to-something-secure

If you’re using Portainer, click on Advanced mode to paste the .env variables.

Go to port :3000 and you should be able to use Reactive Resume v3 right away.

Conclusion

Well… You’ve successfully installed and hosted your own resume builder! Get a feel, play around a bit and you’ll be able to create a beautiful resume without having to worry too much about formatting it and just focus on the content itself. Thank you for reading and have a nice day!

PS: If you want some resume examples, feel free to email me!