Install from scratch
This guide provides comprehensive instructions for installing and configuring Fanoni from scratch on Ubuntu 24.04. It covers all essential components including PostgreSQL database setup, Redis installation, Node.js configuration, and the core Fanoni server and app deployment.
The guide also includes optional but recommended steps for setting up Nginx as a reverse proxy with SSL/TLS support, enabling secure access through custom domains. While primarily written for Ubuntu 24.04, these instructions are compatible with most recent Ubuntu versions.
This installation method is particularly useful for development and testing environments, though for production deployments, the guide notes that using AWS with CDK or other infrastructure-as-code tools is recommended.
Install PostgreSQL
These Postgres installation steps can be skipped if you've already installed Postgres, or are using a database hosted elsewhere. Fanoni server can be configured to connect to remote databases. We'll discuss how to connect to a remote Postgres server below.
Add the PostgreSQL Apt Repository (see PostgreSQL Apt Repository docs)
# Configure the Apt repository
sudo apt install -y postgresql-common
sudo /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
# Install Postgres 16
sudo apt install postgresql-16 postgresql-client-16
Start postgres
sudo pg_ctlcluster 16 main start
Start postgres client
sudo -u postgres psql
Create a "medplum" user:
CREATE USER medplum WITH PASSWORD 'medplum';
Create a "medplum" database:
CREATE DATABASE medplum;
GRANT ALL PRIVILEGES ON DATABASE medplum TO medplum;
Connect to the "medplum" database:
\c medplum
Grant privileges on the "public" schema to the "medplum" user:
GRANT ALL ON SCHEMA public TO medplum;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO medplum;
Exit psql
\q
Install Redis
sudo apt-get install redis-server
Open the Redis config file
sudo vi /etc/redis/redis.conf
Uncomment the "requirepass" line and set a password
requirepass medplum
Restart Redis
sudo systemctl restart redis-server
Install Node.js
Add the Node.js v24.x Ubuntu repository:
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo bash -
Install Node.js
sudo apt-get install nodejs
Build Fanoni
Clone the Fanoni repository
git clone https://github.com/medplum/medplum.git
cd medplum
Install dependencies
npm ci
Configure the app environment
The Fanoni app is a Vite-based single-page application. Environment variables are baked into the static build output at compile time — they are not read at runtime. You must set these variables before running the build command.
Create a .env file in packages/app/ to configure your deployment. The only required variable is MEDPLUM_BASE_URL, which must point to your API server.
cat > packages/app/.env << 'EOF'
# Required: URL of your Fanoni API server
MEDPLUM_BASE_URL=https://api.example.com/
# Optional: Pre-fill a specific OAuth2 client ID for all logins
MEDPLUM_CLIENT_ID=
# Optional: Enable Google Sign-In (provide your Google OAuth2 client ID)
GOOGLE_CLIENT_ID=
# Optional: Enable reCAPTCHA on the sign-in page (provide your reCAPTCHA v3 site key)
RECAPTCHA_SITE_KEY=
# Optional: Allow new users to self-register (set to "false" to disable)
MEDPLUM_REGISTER_ENABLED=true
# Optional: Enable AWS Textract integration
MEDPLUM_AWS_TEXTRACT_ENABLED=false
EOF
Replace https://api.example.com/ with your actual API domain. Leave optional variables empty to use their defaults.
Build the server, app, and necessary dependencies
npm run build:fast
Start Fanoni server
These are abbreviated instructions. For full details, see Run the stack
Start the server in development mode:
cd packages/server
npm run dev
You should now be able to access the Fanoni server at http://localhost:8103/healthcheck.
Start Fanoni app
This command runs Fanoni app using the Vite Dev server. While this is convenient for development and testing, it has two significant limitations:
- The Vite dev server is designed for development, not production use. It serves files inefficiently and will provide an inferior experience for end users.
- The Fanoni app requires several modern browser features that are only available in a 'secure context' (HTTPS), including essential cryptography features. These features will not be available when accessing the app via plain HTTP.
If you plan to access the app and API from other devices on your network, we recommend proceeding to the optional SSL/nginx setup instructions below. This will provide the secure context required for all Fanoni features to function correctly.
In another terminal, start the app in development mode:
cd packages/app
npm run dev
You should now be able to access the Fanoni app at http://localhost:3000.
Optional: Nginx
This "Install from Scratch" guide is designed to help you understand how the different pieces of Fanoni work. It's not our recommended approach for production deployments.
For production, we now have much better options that you should consider:
- For a single-server deployment: Use our Install on Ubuntu guide which uses our official APT repository. This automates the NGINX and SSL certificate setup for you.
- For cloud deployments: Use our Install on AWS guide with AWS CDK, which is our recommended solution for scalable, production-ready environments.
Overview
To run Fanoni securely, you should use SSL/TLS via reverse proxy such as nginx.
To do this, you will need to:
- Install Nginx
- Install Certbot
- Setup SSL
- Add Nginx sites
Before you begin, please identify the domain names you will use for the app and api. For this example, we will use app.example.com and api.example.com.
Update Fanoni server settings
Navigate to your server directory:
cd medplum/packages/server
Update the medplum.config.json file with your new domain:
{
"baseUrl": "https://api.example.com"
// ...
}
Restart the server. If you intend to run the server continuously and survive SSH disconnects, you should consider using nohup:
nohup npm run dev > server.log 2>&1 &
Update Fanoni app settings
If you already created packages/app/.env in the Configure the app environment step above, verify that MEDPLUM_BASE_URL is set to your HTTPS domain. If you skipped that step or used localhost, update the value now — the URL is baked into the build output and must be correct before building.
From the repo root, update packages/app/.env with your production API domain:
cat > packages/app/.env << 'EOF'
# Required: URL of your Fanoni API server
MEDPLUM_BASE_URL=https://api.example.com/
# Optional: Pre-fill a specific OAuth2 client ID for all logins
MEDPLUM_CLIENT_ID=
# Optional: Enable Google Sign-In (provide your Google OAuth2 client ID)
GOOGLE_CLIENT_ID=
# Optional: Enable reCAPTCHA on the sign-in page (provide your reCAPTCHA v3 site key)
RECAPTCHA_SITE_KEY=
# Optional: Allow new users to self-register (set to "false" to disable)
MEDPLUM_REGISTER_ENABLED=true
# Optional: Enable AWS Textract integration
MEDPLUM_AWS_TEXTRACT_ENABLED=false
EOF
Build the app. This will generate a new version of the app in the dist directory:
cd packages/app
npm run build
cd ../..
Start the "preview" server:
cd packages/app
nohup npx vite preview > app.log 2>&1 &
cd ../..
Install Nginx and Certbot
Install nginx and Certbot:
sudo apt-get install nginx certbot python3-certbot-nginx
Setup SSL
Before setting up SSL, make sure your domains point to your server and Nginx is running:
sudo systemctl start nginx
sudo systemctl enable nginx
Get SSL certificates from Let's Encrypt for both domains:
sudo certbot --nginx -d app.example.com
sudo certbot --nginx -d api.example.com
Add Nginx site for app
For the app, we will proxy requests to the Vite preview server running on port 3000.
Create an app config file such as /etc/nginx/sites-available/app.example.com:
# /etc/nginx/sites-available/app.example.com
server {
listen 80;
listen [::]:80;
server_name app.example.com;
# Redirect HTTP to HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://localhost:3000;
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;
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;
}
}
Add Nginx site for api
For the API server, we will proxy requests to the Node.js server running on port 8103.
Create a api config file such as /etc/nginx/sites-available/api.example.com:
# /etc/nginx/sites-available/api.example.com
server {
listen 80;
listen [::]:80;
server_name api.example.com;
# Redirect HTTP to HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location / {
proxy_pass http://localhost:8103;
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;
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;
}
}
Enable the sites
Enable the site:
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo ln -s /etc/nginx/sites-available/api.example.com /etc/nginx/sites-enabled/
Remove the default site:
sudo rm /etc/nginx/sites-enabled/default
Test the configuration:
sudo nginx -t
If the test is successful, reload Nginx:
sudo systemctl reload nginx
Verify the setup
You should now be able to view the API server healthcheck at https://api.example.com/healthcheck
And the app at https://app.example.com