January 05, 2025
8 minutes read
Deploying applications has become increasingly accessible, with a range of free and paid hosting options like Render, AWS, and DigitalOcean. However, for developers who want to learn, experiment, and deploy applications without recurring hosting fees, a Raspberry Pi offers an excellent alternative. This compact yet powerful device allows you to create your own Linux-based server for hosting web applications.
In this blog, we’ll explore how to deploy a TypeScript Node.js application using MySQL (MariaDB on Raspberry Pi) and Prisma ORM on a Raspberry Pi. Additionally, we’ll configure NGINX for reverse proxying and use Ngrok to expose the application to the internet. Let’s dive in!
Raspberry Pi
A low-cost, single-board computer that runs a Linux-based operating system. It’s ideal for creating your own server for IoT or web applications.
Node.js & TypeScript
Node.js is a runtime environment for executing JavaScript on the server, and TypeScript adds static typing to JavaScript, making the codebase more maintainable.
MySQL (MariaDB on Raspberry Pi)
A popular relational database system, MariaDB is a compatible replacement for MySQL and is lightweight enough for a Raspberry Pi.
Prisma ORM
An Object-Relational Mapping (ORM) tool that simplifies database interactions with a type-safe query language and schema migrations.
NGINX
A high-performance HTTP server and reverse proxy server. It helps route traffic to your Node.js application.
Ngrok
A tunneling tool that exposes your locally hosted applications to the internet securely without complex network configurations.
Install the OS
Setup your Raspberry Pi with an OS like Raspberry Pi OS. Use the Raspberry Pi Imager
to find other OS compatible to your Raspberry Pi.
Find the IP Address
Use a tool like Angry IP Scanner
to discover your Raspberry Pi’s IP address. Ensure the Raspberry Pi is connected to the same network as your local machine.
Check Raspberry Pi Status
ping <IP_ADDRESS_OF_RPI>
SSH Into the Raspberry Pi
ssh <username>@<IP_ADDRESS_OF_RPI>
Replace <username>
with your Raspberry Pi’s username and <IP_ADDRESS_OF_RPI>
with the IP address and then enter the password.
Update the System
sudo apt update && sudo apt upgrade
Install Git
Check if git is installed. If not, run the below command to install git
sudo apt install git
To install node js, we will be using nvm
(Node Version Manager). It allows you to quickly install and use different version of node via command line.
Install NVM
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
Verify Installation
nvm --version
Install the Latest LTS Version of Node.js
nvm install --lts
Verify Node.js and npm Installation
node --version
# v22.12.0
npm --version
# 10.9.0
For Raspberry Pi OS, we will be installing MariaDB.
Install the MariaDB SQL Server
sudo apt install mariadb-server
Secure MariaDB Installation
sudo mysql_secure_installation
Follow the prompts to secure your database.
Login to MariaDB Client
sudo mysql
Setup a Root password for MariaDB
First, we need to tell the database server to reload the grant tables.
MariaDB [(none)]> FLUSH PRIVILEGES;
Change the root password with below query.
MariaDB [(none)]> ALTER USER 'root'@'localhost' IDENTIFIED BY '<new_password>';
Replace <new_password>
with your own password.
Use the exit
command to exit from MariaDB CLI.
MariaDB [(none)]> exit;
Bye
Login to MariaDB Client With Root User
sudo mysql -u root -p
Enter the password for the root user.
Let’s create a new database and a user. We will be granting all privileges to the new user for the new database we have created.
Create a Database
MariaDB [(none)]> CREATE DATABASE <database_name>
Create a New User With Password
MariaDB [(none)]> CREATE USER '<new_username>'@'localhost' IDENTIFIED BY '<new_password>';
Grant Privilege To New User Created
MariaDB [(none)]> GRANT ALL PRIVILEGES ON <database_name>.* TO '<new_username>'@'localhost';
Flush The Privileges Table
MariaDB [(none)]> FLUSH PRIVILEGES;
Exit from mysql client using exit
command.
Login With New User
sudo mysql -u <new_username> -p
Enter the password you used while creating the user.
Verify User Can List The Database
MariaDB [(none)]> SHOW DATABASES;
That’s it! We will use this database and user in our application.
Clone Your Github Repository
git clone <your_github_repo_url>
Navigate To Your Project Repository
cd <your_project_name>
Install Project Dependencies
npm install
Compile TypeScript Code
npm run build
Make sure you have configured the
outDir
property in yourtsconfig.json
file. This specifies the directory where the compiled JavaScript code will be generated. By default, it’s commonly set todist
, but you can customize it based on your project structure.
If your project uses environment variables, you need to set them on your Raspberry Pi. You can create a .env
file in the root directory of your project to store all the environment variables.
Create .env
File
touch .env
Update .env
File
sudo nano .env
Enter your Environment Variables
PORT=5000
DATABASE_URL="mysql://<username>:<password>@localhost:3306/<database_name>"
Save the file by pressing Ctrl+O
, then press Enter
, and exit the editor using Ctrl+X
.
If you are using Prisma, all the schema files will be located inside the prisma/schema directory. We will now deploy these schemas to the database.
Run the below command
npx prisma migrate deploy
This command will use the DATABASE_URL
provided in the .env
file to deploy the schemas to the database. You can verify the deployment by logging into the MySQL client and using the command SHOW TABLES;
to list all the tables.
PM2
is a production process manager for Node.js applications which helps in managing and keeping the application online. Install PM2 to manage your Node.js application.
npm install pm2 -g
Install NGINX
sudo apt install nginx
Create a Site Configuration
sudo nano /etc/nginx/sites-available/<your_project_name>
Add the Below Code
server {
listen 80;
server_name <your_raspberrypi_IP>;
location / {
proxy_pass http://localhost:YOUR_NODE_JS_PORT;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
Here’s a breakdown of each part:
listen 80;
This directive tells NGINX to listen on port 80, which is the default port for HTTP traffic.
server_name <your_raspberrypi_IP>;
This specifies the domain name or IP address of your Raspberry Pi. Replace
location / { ... }
This block defines how NGINX should handle requests to the root URL (/). Essentially, this tells NGINX that whenever a request is made to the root, it should be forwarded to the backend (your Node.js application) running on the specified port.
proxy_pass http://localhost:YOUR_NODE_JS_PORT;
This is the key line that forwards incoming requests to your Node.js application. Replace YOUR_NODE_JS_PORT with the actual port where your Node.js app is running (for example, 5000). The requests will be sent to the Node.js application running on the same machine (localhost).
proxy_http_version 1.1;
This sets the HTTP version to 1.1 for the proxy connection, which ensures better handling of certain features like WebSockets.
proxy_set_header Upgrade $http_upgrade;
This header allows WebSocket connections to be upgraded, which is important for real-time applications.
proxy_set_header Connection 'upgrade';
This header is used alongside the Upgrade header to manage WebSocket connections, ensuring that the connection is properly upgraded from HTTP to WebSocket.
proxy_set_header Host $host;
This passes the original Host header from the client request to the backend server. This is useful for applications that rely on the original Host header (e.g., for routing or virtual hosting).
proxy_cache_bypass $http_upgrade;
This ensures that WebSocket connections bypass any caching mechanisms, allowing real-time communication to work without interference from caching.
Save the file by pressing Ctrl+O
, then press Enter
, and exit the editor using Ctrl+X
.
Enable the Site Configuration
sudo ln -s /etc/nginx/sites-available/<your_project_name> /etc/nginx/sites-enabled/
Test NGINX Configuration
sudo nginx -t
If the test is successfull, you will see something like below:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
Restart NGINX Server To Apply the Changes
sudo systemctl restart nginx
Check NGINX Server Status
sudo systemctl status nginx
Navigate to your project
Start Your Application Using PM2
If you have setup a script in package.json
, use the below command:
pm2 start "npm run start"
Or, you can directly run you application using index.js
file in your dist
directory:
pm2 start dist/index.js
You can also check the logs using below command:
pm2 logs
Now, check your app by entering the IP address of your Raspberry Pi in the browser on your local machine. It should work. Make sure both your local machine and Raspberry Pi are connected to the same network; otherwise, it will not work.
Now that you have deployed your app to the Raspberry Pi, you can only access the app from the same network in which the Raspberry Pi is running. To expose it to the internet, we need to use port forwarding.
You can set up port forwarding using your router settings, but in this case, I will be using ngrok. Ngrok
is useful for development, allowing us to run our apps for testing purposes for free.
Make sure to create an account by visiting https://dashboard.ngrok.com/login. You will need the auth token to configure ngrok on the Raspberry Pi.
Install Ngrok
curl -sSL https://ngrok-agent.s3.amazonaws.com/ngrok.asc \
| sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null \
&& echo "deb https://ngrok-agent.s3.amazonaws.com buster main" \
| sudo tee /etc/apt/sources.list.d/ngrok.list \
&& sudo apt update \
&& sudo apt install ngrok
Add your auth token to ngrok configuration file
ngrok config add-authtoken <your_auth_token>
Disable default nginx config file
sudo rm /etc/nginx/sites-enabled/default
Test NGINX configuration
sudo nginx -t
Restart NGINX server to apply the changes
sudo systemctl restart nginx
Deploy your app online
ngrok http 80
This should provide a URL like https://xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx-xxxx.ngrok-free.app/
that forwards traffic to your Node.js app. You can navigate to this URL from any other network and access your application.
In this guide, we successfully deployed a TypeScript Node.js application with MySQL and Prisma on a Raspberry Pi. We configured NGINX as a reverse proxy and used Ngrok to make the application accessible over the internet. With this setup, you have your own cost-effective, self-hosted development server.
This approach is perfect for learning and experimenting with full-stack application deployment, all while gaining valuable experience in server management.
Let me know if you deploy your application using this guide—I’d love to hear about your experience! 🚀