Bootstrap an opinionated, production-ready Bun application environment for Ubuntu.
- Installs Bun, Nginx, UFW, Certbot (via snap), and sets up a Bun app under
/srv/app. - Creates a systemd service
bun-appfor running the Bun app. - Configures Nginx as a reverse proxy to
localhost:3000and serves static files from/var/www/app/dist(or/var/www/htmlif sample app is skipped). - Configures UFW: allows SSH (22), HTTP (80), and HTTPS (443), limits SSH, and prefers the 'Nginx Full' profile.
- Optionally creates a sample Bun app at
/srv/appand enables the bun-app service (setSKIP_BUN_APP=1to skip). - Intended for provisioning Ubuntu servers (22.04+); run as root/sudo with internet access.
- Ubuntu 22.04+ or 24.04, run as root or with sudo.
- Internet access for package and snap installs.
| Software | Version | License |
|---|---|---|
| Bun | 1.3.x | MIT |
| Nginx | 1.24.x | Artistic License 2.0 |
| Certbot | 5.1.x | Apache 2 on GitHub |
Pipe the installer directly to bash (runs as root with sudo)
Important
Piping remote scripts to a shell executes code from the network — review the script before running.
curl -fsSL https://raw.githubusercontent.com/acfatah/ubuntu-bun-server-setup/main/install.sh | sudo bashTo skip sample app:
curl -fsSL https://raw.githubusercontent.com/acfatah/ubuntu-bun-server-setup/main/install.sh | sudo bash -s -- SKIP_BUN_APP=1Or after cloning this repository:
sudo bash install.shWhen done:
- Check Bun:
bun --version - Bun app service:
systemctl status bun-app(logs:journalctl -u bun-app -f) - Nginx default site:
http://<server-ip>serving/var/www/html. - Get HTTPS cert for your Nginx site:
certbot --nginx.
The templates/cloudflare-update-ips.sh
script downloads Cloudflare's IPv4 and IPv6 ranges, converts each line into an allow
directive, and rewrites /etc/nginx/cloudflare-ip-filter.conf before reloading Nginx.
The nginx default config already ships with the include commented out. To enable
this feature, uncomment the include cloudflare-ip-filter.conf; line in the server
block to apply the allowlist to the default host.
If you need the rules applied globally, place the include in the http { ... } block
of /etc/nginx/nginx.conf instead, so every server block inherits it.
Schedule the script with cron (runs as root so it can reload Nginx) by using crontab -e;
for example, adding the following line:
1 2 * * * /etc/nginx/cloudflare-update-ips.sh
This runs shortly after 2:00 AM every day to keep Cloudflare's allowlist current. The script logs warnings to /var/log/cloudflare-update-ips.log if it can’t download enough addresses.
This project includes a Docker-based end-to-end test harness that provisions an Ubuntu systemd environment in a container and runs the installer in realistic scenarios.
- Docker installed and the Docker daemon running.
- Ability to run privileged containers (the test container runs systemd).
- Run commands from the repository root.
From the project root:
make testThis will:
- Build a test image from
tests/docker/Dockerfile. - Start short-lived, privileged containers mounting this repo read-only at
/workspace. - Execute the test scripts under
tests/docker/scripts/*.sh.
You can target a specific scenario by calling the test runner directly:
tests/docker/run.sh test_root_guard
tests/docker/run.sh test_default
tests/docker/run.sh test_skip_sampleThe .sh suffix is optional; both test_default and test_default.sh work.
test_root_guard.sh— ensures the installer fails when run as a non-root user.test_default.sh— runs a default install and verifies systemd units, application files, Nginx configuration, HTTP response ("Hello Bun"), andcertbotavailability.test_skip_sample.sh— runs the installer withSKIP_BUN_APP=1and checks that Nginx serves/var/www/htmland returns the expected"Hello World"content.
You can override the test image name with the BUN_INSTALLER_TEST_IMAGE
environment variable if you want to reuse or inspect the image:
BUN_INSTALLER_TEST_IMAGE=my-registry/ubuntu-bun-installer-tests make testSet any to 1 to skip:
-
SKIP_BUN_APP=1— Do not create sample/srv/appand use/var/www/htmlfor Nginx static root. You are now responsible for building/copying your own Bun-generated HTML assets into/var/www/html.Build steps typically look like
bun install && bun run buildfrom your project and thencp -R dist/* /var/www/htmlbefore managing your own Bun service.
Example:
sudo SKIP_BUN_APP=1 bash install.sh-
The default Bun app runs from
/srv/appand executesbun run start. -
Static files are served by Nginx from
/var/www/app/dist(or/var/www/htmlif sample app is skipped). Place your built assets in that directory and set the correct permissions:sudo chown -R www-data:www-data /var/www/app/dist sudo find /var/www/app/dist -type d -exec chmod 755 {} + sudo find /var/www/app/dist -type f -exec chmod 644 {} + -
Static files are served at
/and API is proxied to Bun at/api. -
To use your own Bun app, replace
/srv/appcontents and update the systemd service ExecStart as needed. -
If you skip the default app, set up your own Bun application and systemd unit file. Example:
[Unit] Description=Bun App After=network.target [Service] Type=simple WorkingDirectory=/srv/app ExecStart=/root/.bun/bin/bun run start Restart=always RestartSec=3 User=root Environment=NODE_ENV=production Environment=INSTANCE_ID= # Value from /etc/environment StandardOutput=journal StandardError=journal SyslogIdentifier=bun-app [Install] WantedBy=multi-user.target