Containers

Install OpenShip on Ubuntu 26.04 / 24.04

OpenShip is a self-hostable deployment platform: you point it at a repository or a folder, and it builds the app, ships it, routes traffic to it, and terminates TLS with a Let’s Encrypt certificate. Think of the workflow you get from Vercel or Netlify, running on hardware you own. The project crossed 8,000 GitHub stars within weeks of its first public release, and the whole control plane installs from a single script.

Original content from computingforgeeks.com - post 170329

This guide walks through how to install OpenShip on Ubuntu and self-host it end to end: install Docker, install the OpenShip CLI, run the control plane as a service, put the dashboard behind HTTPS, create the first admin account, and register a server you can deploy to. Every command here was run on Ubuntu 26.04 (Resolute Raccoon) in July 2026 against OpenShip 0.3.0. The install path is identical on 24.04 LTS, where the Docker apt source resolves the noble codename automatically.

What you get with self-hosted OpenShip

OpenShip has two ways to run. The desktop app runs the control plane on your own machine only while the app is open, which suits a solo developer who just wants push-to-build without an always-on server. The self-hosted server install is what this guide covers: an always-on control plane you reach from anywhere, with team access and the ability to host apps on the box. You want the server install once you need push-to-deploy, a shared dashboard, or a public endpoint.

The CLI install runs OpenShip as a single Bun process with an embedded database and an in-process job runner, so there is no separate Postgres or Redis service to manage. OpenShip also ships a Docker Compose variant that runs Postgres and Redis as containers, and a separate OpenResty edge for routing and certificates, but that is a different install path from the CLI service this guide sets up. Application builds run in Docker either way, which is why Docker is the one real prerequisite.

Prerequisites

OpenShip’s control plane is light, but it builds application images with Docker, and a Docker build plus a Node or Postgres base image is what actually drives the sizing. On the lab boxes used here, the build step is the memory spike, not the idle API.

  • Ubuntu 26.04 or 24.04 LTS, a sudo-capable user, and SSH access.
  • 2 vCPU and 4 GB RAM is a comfortable floor for the control plane plus one small build at a time. A box that will build several apps concurrently, or build a heavy image, wants 4 vCPU and 8 GB or more. The control plane alone sits under 1 GB; the headroom is for builds.
  • 20 GB of disk to start. Docker images and build cache grow quickly, so plan for more if you host several apps.
  • A domain with an A record pointing at the server, and port 80 and 443 reachable, so the dashboard can be served over HTTPS. Any DNS provider works.
  • Docker Engine, installed in the next step.

Set reusable shell variables

A couple of values repeat across the install, so export them once at the top of your SSH session and paste the rest as-is. Change the domain and email to your own:

export OS_DOMAIN="deploy.example.com"
export ACME_EMAIL="[email protected]"

Confirm they are set before running anything else:

echo "Domain: ${OS_DOMAIN}"
echo "Email:  ${ACME_EMAIL}"

These hold only for the current shell. Re-run the two export lines if you reconnect.

Install Docker Engine

OpenShip builds every app in Docker, so install Docker Engine from Docker’s official repository rather than the older distro package. Add the repository key and source:

sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list

Docker publishes a package for the Ubuntu 26.04 resolute codename, so the same commands work on both LTS releases. Install the engine and the Compose plugin, then add your user to the docker group:

sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker $USER

Log out and back in so the group change takes effect, then confirm the daemon is up:

docker --version
systemctl is-active docker

The version prints and the service reports active:

Docker version 29.6.2, build dfc4efb
active

With the build engine in place, install OpenShip itself.

Install the OpenShip CLI

The CLI is the whole product: it installs the API and the dashboard, runs them as a service, and manages the instance. The install script pulls the Bun runtime it needs and wires the openship binary for you:

curl -fsSL https://get.openship.io | sh

The script bootstraps Bun, installs the CLI, and prints the path to add to your shell. Make the binary available on your PATH for the current session:

export PATH="$HOME/.bun/bin:$PATH"

Add that same line to your ~/.bashrc so it survives new logins. Check the CLI is on your path:

openship --version

It reports the installed release:

0.3.0

With the CLI on your path, bring the platform online.

Run the OpenShip control plane

Start the service. On a first run OpenShip downloads the dashboard, registers a systemd user unit that survives reboots, and brings the API up on port 4000 and the dashboard on port 3001:

openship up

Once it settles, check the local service and the API health in one place:

openship status

The service shows as running, the health check reads ok, and the ports are listed:

  Service       running
  Manager       systemd-user
  API port      4000
  Dashboard port 3001
  API           http://localhost:4000/api
  Health        ok
  Mode          self-hosted
  Machine       openship

Here is what that looks like end to end, from the Docker version through the OpenShip service status:

OpenShip 0.3.0 version and openship status output on Ubuntu 26.04

The API is bound to localhost only, which is exactly what you want before a reverse proxy and a login are in place. The next two steps make it reachable and locked down.

Serve OpenShip over HTTPS

Never expose the dashboard over plain HTTP. Because a public instance requires a login, the dashboard needs a valid certificate for the login flow and the session cookie to work correctly. Put Nginx in front of the dashboard port and let Let’s Encrypt issue the certificate.

Install Nginx and Certbot:

sudo apt-get install -y nginx certbot python3-certbot-nginx

Create the site definition that proxies to the dashboard. Open the file:

sudo vim /etc/nginx/sites-available/openship

Add the following server block. The SITE_DOMAIN_HERE placeholder is substituted from your shell variable in the next command, and the two WebSocket headers keep the dashboard’s live build logs streaming:

server {
    listen 80;
    server_name SITE_DOMAIN_HERE;

    location / {
        proxy_pass http://127.0.0.1:3001;
        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;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_http_version 1.1;
        proxy_read_timeout 300s;
    }
}

Substitute your real domain into the file, enable the site, drop the default, and reload:

sudo sed -i "s/SITE_DOMAIN_HERE/${OS_DOMAIN}/g" /etc/nginx/sites-available/openship
sudo ln -sf /etc/nginx/sites-available/openship /etc/nginx/sites-enabled/openship
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

Issue and install the certificate. Certbot’s Nginx plugin uses the HTTP-01 challenge, which needs port 80 reachable and the A record pointing at this server:

sudo certbot --nginx -d "${OS_DOMAIN}" --non-interactive --agree-tos --redirect -m "${ACME_EMAIL}"

Certbot rewrites the vhost to listen on 443, installs the certificate, and adds the HTTP-to-HTTPS redirect. Before you switch OpenShip to public mode, close every port except SSH and the web ports, because public mode binds the dashboard to all interfaces on port 3001. Ubuntu ships UFW for this:

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable

With UFW default-deny in place, the raw dashboard port 3001 and the API port 4000 are unreachable from outside, so both are only served through Nginx. This is not optional. Public mode binds 3001 to every interface, and the next flag trusts a client-IP header, so anything that can reach 3001 directly would bypass HTTPS and defeat the login rate limiting. Now restart OpenShip in public mode so it binds the dashboard for external access, proxies the API same-origin, and enforces login:

openship stop
openship up --public-url "https://${OS_DOMAIN}" --trust-proxy

The --trust-proxy flag tells OpenShip to read the real client address from the header Nginx sets, which is what makes per-client rate limiting accurate behind the proxy.

Private servers and wildcard certificates

If the box sits on a private network with no public port 80, or you are behind NAT, the HTTP-01 challenge cannot reach it. Use a DNS-01 challenge instead, which proves domain ownership with a DNS TXT record and needs no inbound HTTP. Certbot ships DNS plugins for Cloudflare, Route 53, DigitalOcean, Google Cloud DNS, and others; install the plugin for your provider, point Certbot at an API credentials file, and issue with certonly --dns-<provider>. OpenShip also has a built-in managed edge (openship up --managed-edge) that installs OpenResty and requests a Let’s Encrypt certificate on the box itself, if you would rather it handle routing than run your own Nginx.

Create the first admin and sign in

Run the interactive setup. It walks through the instance login, the domain, and installs the boot service:

openship

The wizard asks for the admin account first, so set the admin name, email, and password. It then asks how the instance should be reachable: pick the public option, and choose to bring your own domain when the domain question follows, since you already handled TLS with Nginx. When it finishes, open the dashboard at your domain and sign in. The login page is served over HTTPS with the certificate you just issued:

OpenShip self-hosted dashboard login page served over HTTPS

After signing in you land on the dashboard home, with panels for projects, deployments, and connected apps. This is the control plane you will drive everything from:

OpenShip self-hosted dashboard home showing projects and deployments

The dashboard is only the control plane. Your apps run on a target you register next.

Register a server to deploy to

OpenShip runs your apps on a server it reaches over SSH. For a real setup this is a separate box from the control plane, a VPS or a machine in your rack, added with its host, user, and an SSH key. The default user is root, which matters because OpenShip writes deploy state under /var/lib/openship. Test the connection first without saving it:

openship server test --host 10.0.1.60 --user root --auth-method key --key-path ~/.ssh/id_ed25519

When the connection succeeds, add the server:

openship server add --host 10.0.1.60 --name prod-1 --user root --auth-method key --key-path ~/.ssh/id_ed25519

OpenShip prints the new server’s ID next to the name. The health check and deploys reference a server by that ID, not by its name, so copy it from the add output or list your servers to find it:

openship server list
openship server check b1f2c3d4-0000-0000-0000-000000000000

The check reports which components are present and healthy on the target. Git, rsync, and Certbot are the ones OpenShip leans on, and a fresh Ubuntu box has them or installs them quickly:

  component  installed  healthy  version  optional
  git        yes        yes      2.53.0   no
  certbot    yes        yes      4.0.0    yes
  rsync      yes        yes      3.4.1    yes
  Server is ready.

With a healthy target registered, you can point a project at it.

Create a project and configure a deployment

With a server registered, create a project from a Git repository, a Git URL, or a local folder. The dashboard’s New Project screen imports the source and detects the stack. From the project page, Deploy now opens the deploy flow: pick the target server, and OpenShip inspects the code and fills in the build configuration for you. For a Node.js app it detects the framework, sets the base image, and proposes the install and start commands and the port:

OpenShip deploy configuration screen with Node.js framework detected and build settings

You set the domain the app will answer on, choose a free subdomain or your own custom domain, and confirm the exposed port. The same flow is scriptable from the CLI: openship init links a folder to a project, and openship deploy --watch triggers a build and streams the logs. If you prefer a declarative config, openship config init scaffolds an openship.json that captures the framework, build, and domain settings in the repository.

Where OpenShip fits, and what to expect on an early release

OpenShip is young. The 0.3.x line is only weeks old, and it shows in the parts that matter most for a first look: the control plane is solid and the install is genuinely a one-liner, but the server-deploy pipeline is still settling. In testing on a single-box lab, Docker image builds ran cleanly through the build stage, while the deploy step was less predictable than the rest of the product. That is the normal shape of a fast-moving project at this stage, not a reason to skip it. If you want the smoothest path today, the desktop app is the most polished way to try the build-and-deploy flow before you commit a server to it. The run mode is also evolving quickly, so re-check the install steps when you upgrade: a later release can bring its own edge on ports 80 and 443, which would collide with the Nginx setup here.

What the self-hosted server install already does well is give you an always-on, HTTPS-fronted control plane with real login, server registration over SSH, and stack detection, all from a single CLI. If your goal is a self-hosted alternative to the managed deploy platforms, OpenShip is worth watching closely and easy to stand up now. It also sits naturally next to the other self-hosted infrastructure you probably already run: a self-hosted Git forge with its own CI for the source and pipelines, and a Docker fleet manager for the containers OpenShip builds. For the apps themselves, having the Node.js runtime installed on your target servers keeps local builds and debugging simple. Revisit OpenShip as the deploy pipeline matures, because the foundation it is building on is the right one.

Keep reading

Best UI Applications for Managing Docker Containers Containers Best UI Applications for Managing Docker Containers Install Docker and Run Containers on Ubuntu 24.04|22.04 Containers Install Docker and Run Containers on Ubuntu 24.04|22.04 Install UniFi OS Server on Ubuntu 24.04 LTS Containers Install UniFi OS Server on Ubuntu 24.04 LTS NVIDIA GPU Monitoring with DCGM Exporter, Prometheus, and Grafana DevOps NVIDIA GPU Monitoring with DCGM Exporter, Prometheus, and Grafana Best Platform Engineering Books for 2026 Books Best Platform Engineering Books for 2026 EKS Kubernetes Persistent Storage with EFS Storage Service AWS EKS Kubernetes Persistent Storage with EFS Storage Service

Leave a Comment

Press ESC to close