How to Deploy a Production-Ready File Server on a VPS for Free
This article walks through deploying a self-hosted file server on a VPS with a clean production setup: build the app, configure environment variables, run it with systemd, place Nginx in front, and secure the server with SSL and a firewall.
Running your own file server is not only about saving on storage costs. It is also about control: you decide where files live, how uploads are handled, what gets exposed publicly, and how the server is secured.
For this guide, we will use a Spring Boot based file server pattern similar to FiloraFS. The exact app name does not matter much here — the deployment flow is what matters. You can apply these steps to most Java backend file services.
What you will build
By the end of this tutorial, you will have a file server that:
- accepts file uploads through a backend API
- stores files on disk or in a mounted directory
- runs continuously on a VPS using systemd
- serves traffic through Nginx
- uses HTTPS with a free Let’s Encrypt certificate
- is protected by a basic firewall setup
Prerequisites
- A VPS with Ubuntu 22.04 or similar
- Java 21 installed on your local machine and server
- Git access to your project repository
- A domain name, if you want HTTPS with Nginx
- Your application jar built and ready to deploy with FiloraFS-Lite
1. Prepare the application for production
Start by making sure your app has a clean production configuration. Avoid hardcoding secrets or server-specific paths. Use environment variables or a separate production config file instead.
A typical Spring Boot setup for a file server might look like this:
# application-prod.properties
spring.application.name=filorafs
server.port=8080
storage.local.path=/opt/filora/uploads
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB
spring.datasource.url=jdbc:mysql://localhost:3306/filorafs
spring.datasource.username=filora_user
spring.datasource.password=${DB_PASSWORD}
If your project does not use a database, remove the datasource block and keep only the storage and multipart settings.
2. Get a free VPS
For a low-cost test deployment, you can use a free-tier VPS from a cloud provider that supports Linux instances. Any machine with public IP access, SSH access, and enough storage for your files will work.
Once the VPS is ready, download the SSH key or note the login credentials so you can connect securely.
3. Connect to the server and install dependencies
SSH into the server and install the tools required to run your backend.
ssh -i your-key.key ubuntu@your-vps-ip
sudo apt update && sudo apt upgrade -y
sudo apt install openjdk-21-jdk nginx ufw -y
If you plan to build the application directly on the server, also install Git and Maven.
sudo apt install git maven -y
4. Upload the jar and create an app directory
Create a clean folder for your application. Keeping the jar, logs, and environment file inside one directory makes maintenance easier.
sudo mkdir -p /opt/filora
sudo chown -R $USER:$USER /opt/filora
Then copy your jar file into that directory.
scp target/filorafs.jar ubuntu@your-vps-ip:/opt/filora/
5. Add environment variables
Put your secrets and server-specific values in a dedicated .env file. This keeps your service config simpler and avoids exposing values in the codebase.
# /opt/filora/.env
DB_PASSWORD=your_db_password
SERVER_PORT=8080
STORAGE_PATH=/opt/filora/uploads
6. Run the app manually first
Before creating a systemd service, verify that the jar starts correctly in the terminal.
cd /opt/filora
source .env
java -jar filorafs.jar
If the application starts without errors, you are ready to make it persistent.
7. Create a systemd service
systemd keeps the file server alive after reboot and restarts it automatically if the process crashes.
sudo nano /etc/systemd/system/filorafs.service
Paste the following configuration:
[Unit]
Description=FiloraFS File Server
After=network.target
[Service]
User=ubuntu
WorkingDirectory=/opt/filora
EnvironmentFile=/opt/filora/.env
ExecStart=/usr/bin/java -jar /opt/filora/filorafs.jar
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Then enable and start it:
sudo systemctl daemon-reload
sudo systemctl enable filorafs
sudo systemctl start filorafs
sudo systemctl status filorafs
8. Put Nginx in front of the app
Nginx is useful even for a simple backend. It gives you a stable public entry point, handles HTTPS termination, and lets you define upload size limits.
Create a new site config:
sudo nano /etc/nginx/sites-available/filorafs
Example configuration:
server {
listen 80;
server_name yourdomain.com;
client_max_body_size 100M;
location / {
proxy_pass http://localhost: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;
}
}
Enable the site and reload Nginx:
sudo ln -s /etc/nginx/sites-available/filorafs /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
9. Add HTTPS with Let’s Encrypt
If your server is publicly accessible, SSL is not optional. Use Certbot to get a free certificate and let Nginx manage the HTTPS configuration.
sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d yourdomain.com
After setup, Certbot can renew certificates automatically.
10. Lock down the firewall
Use UFW to expose only the ports you actually need.
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
If your backend should never be reached directly, do not expose port 8080 publicly. Let Nginx talk to the app internally on localhost only.
11. Test everything
Once the service is live, test upload, download, and delete flows from your browser or API client.
- Check app logs:
journalctl -u filorafs -f - Check Nginx logs:
sudo tail -f /var/log/nginx/error.log - Confirm HTTPS loads correctly
- Try a real file upload and verify the file appears in your storage directory
Common mistakes
Using localhost in production config
Inside a VPS, localhost is still fine for the app itself, but external clients should connect through your domain and Nginx.
Skipping file permissions
Make sure the service user can write to the upload directory, otherwise file storage will fail even if the app starts.
Exposing the app port directly
Let Nginx handle public traffic and keep the backend private whenever possible.
Final thoughts
A file server becomes much easier to manage once the deployment pattern is clean. Build the jar, move it to a VPS, run it with systemd, place Nginx in front, and secure the machine properly. That is the difference between a demo and a usable production setup.
If you are using a starter like FiloraFS, this workflow gives you a reliable path from local development to a real deployed service without unnecessary complexity.