Deploy app in VPS via Portainer 🤩

updated at Sat, Mar 15, 2025

What is Portainer ?

Portainer is a GUI-based tool for managing Docker.

I’d like to host projects using Docker on a VPS, and Portainer makes maintenance much easier.

Table of contents

1. Prerequest

2. Prepare a source code

3. Register Image to GHCR

4. Create a container by Portainer

5. My trouble shooting

1. Prerequest

2. Prepare a source code

Create a directory in VPS

~$ mkdir ~/home/<user name>/<project name>

Then add a source code with Dockerfile.

reference

Dockerfile

# Use the latest LTS version of Node.js
FROM node:18-alpine
 
# Set the working directory inside the container
WORKDIR /app
 
# Copy package.json and package-lock.json
COPY package*.json ./
 
# Install dependencies
RUN npm install
 
# Copy the rest of your application files
COPY . .
 
# Expose the port your app runs on
EXPOSE 3000
 
# Define the command to run your app
CMD ["npm", "start"]

3. Register Image to GHCR

However Portainer handles containers like Docker Compose, Portainer itself doesn’t build Docker Image.

So build Docker Image then register it to Github Container Registory(GHCR) to use it in Portainer’s stack.

I prefer to use GHCR because it allows me to host multiple private Images.

  1. Build Image. *All process are done under project’s directory.

    $ docker build -t <app name>:<version number> .
    
  2. Get an Personal Access Token in Github to register Docker Image to GHCR.

  3. Access GHCR by docker login. Let’s say we store Personal Access Token in ~/ghcr.txt

    $ cat ~/ghcr.txt | docker login ghcr.io -u <github user name> --password-stdin
    
  4. Push Docker Image to GHCR.

    $ docker tag <Image id> ghcr.io/<github user name>/<repository>/<Image name>:<version number>
    
    $ docker push ghcr.io/<github user name>/<repository>/<Image name>:<version number>
    

4. Create a container by Portainer

Create a new stack to Portainer.

If the app use .env, add environment in the stack.

Stack

services:
  app:
    image: ghcr.io/<github user name>/<repository>/<image name>:<version number>
    container_name: <container name>
    volumes:
      - /home/<user name>/<...directory>/<project name>/node_modules:/app/node_modules
    ports:
      - "3000:3000"
    stdin_open: true
    tty: true

Access the web application from <vps-address>:3000.

5. My trouble shooting

Actually, I was faced un error.

The error-log said that react-start script not found.

It meant that node_modules wasn’t exist in the container.

So I updated Dockerfile like below,

# Use the latest LTS version of Node.js
FROM node:18-alpine
 
# Set the working directory inside the container
WORKDIR /app
 
# Copy package.json and package-lock.json
COPY package*.json ./
 
# Copy the rest of your application files
COPY . .

# Install dependencies
RUN npm install
 
# Expose the port your app runs on
EXPOSE 3000
 
# Define the command to run your app
CMD ["npm", "start"]

To debug this situation, I added command: sh to Portainer’s stack.
Since the container was stopped immediately after it was created, I can pose this process by adding it.

Don’t forget to remove it after debugging after then.

Happy dev!😉

Update at Sat, Mar 15, 2025

My initial configuration of the volume in docker-compose.yaml wasn’t correct.

volumes:
      - ./node_modules:/app/node_modules

Should be like this.

volumes:
      - /home/<user name>/<...directory>/<project name>/node_modules:/app/