Vai al contenuto
Home / Blog /Tutorial / From repository to cloud: client preview and deploy with Harbor

From repository to cloud: client preview and deploy with Harbor

The whole loop around a Maho project: create the repository, develop locally with Harbor, hand the client an ephemeral preview link, push to main and publish with the deploy button in the MageMEGA cloud panel.

Leggi in italiano

A store is only as good as the path the code takes to reach it. Between an empty repository and the day the client clicks a live URL there are four stops: the repository, your machine, a preview link and the production server. Harbor, the Docker Compose launcher for Maho, covers the local stop; a Cloudflare tunnel turns your laptop into a URL you can hand over; MageMEGA takes care of the last mile with a deploy button. This tutorial walks the whole loop on Linux, macOS and Windows with WSL, starting from an empty repository. It does not matter what the store sells, only that a change travels from the editor to a public page without surprises. The whole workflow fits in four commands:

git push origin main                          # store the change
./vendor/bin/harbor tunnel quick --autoconfig # show it to the client
# ... then press Deploy in the MageMEGA panel # publish it

⚓ Ship’s log. Every departure needs a port that knows the ship, a dock that holds it and a lighthouse the client can see from the shore. Here the repository is the port, Harbor is the dock and the tunnel is the lighthouse.

Requirements

The examples use Harbor, MageMEGA’s open source Docker Compose launcher, inspired by Laravel Sail.

On the host you need:

  • Docker with the Compose v2 plugin: Docker Desktop on macOS and Windows, Docker Desktop or Docker Engine on Linux. Harbor prefers docker compose and falls back to docker-compose.
  • PHP >= 8.3, only to run Composer, to create the project and add Harbor. Everything else runs inside the container, which ships PHP 8.4 by default.
  • Git, with an account on GitHub, GitLab or Bitbucket.
  • A MageMEGA account for the last step (optional if the local preview is all you need).

Windows: work inside WSL

On Windows, run every command in this tutorial inside a WSL2 distribution (Ubuntu is the usual choice):

  • Install Docker Desktop with the WSL2 backend, or Docker Engine inside the distribution.
  • Install Git and PHP/Composer in the distribution, not only on the Windows side.
  • Keep the project in the Linux filesystem, for example ~/projects/my-store. Do not work under /mnt/c: file watching is slow there and the containers see a different mount.
  • Open https://localhost:8443 from the Windows browser as usual: WSL2 forwards localhost.

Quick start: from an empty repository to a public page

1. Create the repository

Start on the hosting side, not on your laptop. Create an empty repository on GitHub (GitLab and Bitbucket work the same way): no README, no .gitignore, no license, so the first push has nothing to reconcile. Name it after the project, for example my-store, and keep it private while it is a client project.

Copy the SSH URL, it looks like [email protected]:<owner>/my-store.git. MageMEGA clones over SSH only, so we stay on SSH from the start. To push yourself over SSH you also need your own key added to the Git host: the deploy key MageMEGA gives you is a separate, server-side key, added in step 6.

2. Create the Maho project and add Harbor

composer create-project mahocommerce/maho-starter my-store
cd my-store
composer require --dev empiricompany/harbor   # or :dev-main for the latest version

3. Start the local stack

harbor init generates the .harbor/ directory with the project files (.env, compose.yaml, docker.override.yaml, docker.install.yaml). Then build and start the stack: the first build takes 5 to 20 minutes, the perfect moment to grab a coffee rather than press Ctrl+C.

./vendor/bin/harbor init
./vendor/bin/harbor up -d --build
./vendor/bin/harbor ps

Install the PHP dependencies and Maho inside the container:

./vendor/bin/harbor composer install

./vendor/bin/harbor maho install --license_agreement_accepted yes \
  --locale en_US --timezone UTC --default_currency USD \
  --db_host db --db_name maho --db_user maho --db_pass maho \
  --url 'https://localhost:8443/' --use_secure 1 --secure_base_url 'https://localhost:8443/' \
  --use_secure_admin 1 --admin_username admin --admin_password 'change-me-strong' \
  --sample_data 1

The store is now on https://localhost:8443/. From here you develop normally: edit the files, then run harbor maho cache:flush (and harbor maho migrate after installing a module). The full Harbor workflow, from the CLI to optional profiles, is in Local Maho development with Harbor; here we only use it as the engine of the loop.

4. Connect the repository and push the first commit

composer create-project writes the project but does not create a Git repository, so initialize it and point it at the remote:

git init
git branch -M main
git remote add origin [email protected]:<owner>/my-store.git
git add -A
git commit -m "Initial Maho project with Harbor"
git push -u origin main

Run git status before the first commit and make sure that vendor/ and the generated .harbor/ files are not staged. harbor init writes .harbor/.gitignore, which excludes the generated compose.yaml and, above all, .harbor/.env: that file holds the tunnel token and the database password and must never be pushed.

A tunnel exposes the local store on a public HTTPS URL without opening any port on your router. The quick tunnel needs no Cloudflare account:

./vendor/bin/harbor tunnel quick --autoconfig

Harbor starts the tunnel and prints a URL like https://random-words-here.trycloudflare.com. --autoconfig points Maho’s base URLs at it, disables the base-URL auto-redirect, then reindexes and flushes the cache; without the flag Harbor only prints the commands to run. Copy the URL, send it to the client and let them click around.

Emails sent from the preview are still captured by Mailpit on your machine, so the client will not receive real messages: useful for testing order confirmations without spamming anyone. Read them at http://localhost:8025.

The quick URL is ephemeral: it disappears when the tunnel stops. For a demo you repeat, use a named tunnel with a fixed hostname instead:

./vendor/bin/harbor tunnel up --autoconfig

tunnel up reads HARBOR_CLOUDFLARED_TOKEN and HARBOR_CLOUDFLARED_HOSTNAME from .harbor/.env. When the client is done, close the gangway and put Maho back on localhost:

./vendor/bin/harbor tunnel down --autoconfig

⚓ Ship’s log. The tunnel is a gangway lowered to the dock, not a new pier: the store is still on your laptop, it is reachable from the shore only while the gangway is down.

6. Configure MageMEGA once, then deploy with a button

Open the site in the MageMEGA cloud panel and connect it to the repository. The panel is part of the MageMEGA hosting: if you do not know it, the overview is on the managed MahoCommerce hosting page. This is a one-time job.

  1. Go to Source → Deploy Key and copy the public key.
  2. Add it to the Git repository as a deploy key, so the server may clone the code:
    • GitHub: Settings → Deploy keys → Add deploy key. Tick “Allow write access” only if the server must push.
    • GitLab: Settings → Repository → Deploy keys.
    • Bitbucket: Settings → Access keys.
  3. Go to Source → Repository and fill in:
    • Repository URL: the SSH URL, [email protected]:<owner>/my-store.git. The panel accepts SSH URLs only, because the deploy key cannot authenticate over HTTPS.
    • Branch: main.

Before saving, the panel reminds you of two things: the deploy key must already be added to the repository, and the branch must already exist on the remote. Both are true after step 4. Click Save & Deploy: the server fetches the code and runs the first deployment. The Deployment Log tab shows what happened.

7. The daily cycle

From now on every change follows the same short path: develop, preview, push, deploy.

# 1. edit the project, then clear the cache
./vendor/bin/harbor maho cache:flush

# 2. see it on the preview URL while you work
./vendor/bin/harbor tunnel quick --autoconfig

# 3. store the change and send it to the server
git add -A
git commit -m "Add the new homepage banner"
git push origin main

Then open the site card in the MageMEGA panel and press Deploy. The server fetches main and redeploys; the progress and the result are in Deployment Log. Stop the preview tunnel when you no longer need it:

./vendor/bin/harbor tunnel down --autoconfig

The loop at a glance

repository  →  harbor up -d        →  harbor tunnel quick  →  git push origin main  →  Deploy
   holds          runs locally           shows the client          stores the change      publishes

Four tools, one job each: the repository stores, Harbor launches, the tunnel shows, MageMEGA publishes. The client gets a link, the code stays in one place and the button does the rest.

⚓ Last ship’s log. The port is marked on the chart, the dock is ready and the lighthouse is lit. All that is left is to hoist the sail and press Deploy. Fair winds.

Troubleshooting

./vendor/bin/harbor doctor      # validate Docker, Compose and the stack
./vendor/bin/harbor ps          # see what is running
./vendor/bin/harbor logs app    # PHP errors and Caddy access log
./vendor/bin/harbor config      # show the effective Compose configuration
  • The tunnel URL does not open. Wait a few seconds after tunnel quick: cloudflared needs to register with the edge. Check harbor logs cloudflared-quick.
  • Maho links point to localhost after a tunnel. You forgot --autoconfig, or you ran tunnel down without it and the base URLs stayed on the public host.
  • The MageMEGA deployment fails on clone. The deploy key is missing on the repository, or the Repository URL uses https:// instead of the SSH form.

Editor's note.Text prepared with the help of AI and checked against the source before publication. It can still contain mistakes: if you find one, report it.Read the manifesto

Does your store still sell?

Tell us where you are starting from: we reply with a first assessment, no strings attached.

Ask for the store assessment