To deploy a monorepo on Light Cloud, create one app per folder from the same GitHub repository and set each app's Root directory to its folder. Light Cloud detects the framework inside that folder, builds only that folder, and from then on a push redeploys only the apps whose folder changed. Here that is a React site in apps/web and an Express API in apps/api, deployed as two apps from one repository.
What you will build
Two apps from one repository, grouped in a folder in the console:
-
apps/api, an Express service that answers JSON, deployed as a container, -
apps/web, a React page built with Vite, served as a static site, which calls the API.
Source code: github.com/light-cloud-com/tutorial-monorepo.
Before you start
- A GitHub account.
- A Light Cloud account at console.light-cloud.com with GitHub connected (Deploy From GitHub shows how). The free plan allows three server apps, and the API here is one.
- Git and a terminal. On Windows, use PowerShell 7.
Step 1: Get the example repository
Open the example repository on GitHub, click Fork , then Create fork. Clone your fork, with your GitHub username in place of YOUR-USERNAME:
$ git clone https://github.com/YOUR-USERNAME/tutorial-monorepo.git
$ cd tutorial-monorepo
PS> git clone https://github.com/YOUR-USERNAME/tutorial-monorepo.git
PS> cd tutorial-monorepo
The layout is one folder per app, and nothing at the root that would build:
apps/
api/ Express service: server.js, package.json, package-lock.json
web/ React site built with Vite: index.html, src/, package.json, package-lock.json
README.md
Each folder has its own package.json and lock file. That matters: Light Cloud builds an app from its Root directory only, and files outside it are neither built with the app nor watched for changes.
Step 2: Make a folder for the project
A folder keeps the two apps together in the console. Click New... at the top of the sidebar, then New Folder , name it monorepo and click Create folder :
The folder appears in the sidebar. Its + button adds things to it; that is where the next two steps start:
Step 3: Deploy the API from apps/api
- Click the folder's + , then Deploy from GitHub. In Search your repositories... , type
tutorial-monoand pick tutorial-monorepo.
Light Cloud looks at the repository root, finds no project there, and says so:
- Click the folder button next to Root directory. It lists the repository's folders; expand
appswith + and click api :
Detection runs again, inside apps/api this time: Express / Backend , based on package.json and package-lock.json, Runs on a server. The Port under Advanced is already 8080, which is what server.js listens on through process.env.PORT.
- The Name still reads
tutorial-monorepo, the repository name, because the folder does not rename the app. Typemonorepo-api; the name becomes part of the address. Then click Deploy :
A container build takes longer than a static site: the build finished in 1 minute 5 seconds, and the API answered at https://main-monorepo-api-examples.light-cloud.io two minutes after I clicked Deploy (examples is my workspace; yours is in the address instead). Open yours in the browser: it answers with {"service":"api","version":1,"time":"..."}. Copy the address; the web app needs it.
Step 4: Deploy the web app from apps/web
Click the folder's + again, then Deploy from GitHub , and pick tutorial-monorepo once more. Same repository, second app.
This time type
apps/webinto Root directory and press Enter. Detection finds React / Frontend and fills innpm ci,npm run buildand the output folderdist.Type
monorepo-webas the Name.Open Advanced - build settings, environment variables, domain, scaling , click Add variable and add
VITE_API_URLwith your API address from Step 3 as the value. Vite readsVITE_variables when it builds the site, so the page knows where its API lives. Then click Deploy :
The static build took 19 seconds, and the page was live 60 seconds after the click, at https://main-monorepo-web-examples.light-cloud.io. It calls the API and shows the answer:
The folder page shows both apps from the one repository:
Step 5: Push a change to one app
Now the part that makes a monorepo pleasant: a push redeploys only the app whose folder it touched. Change the version line in the web app:
<p>This page is the web app. Version 2.</p>
Commit and push:
$ git commit -am "web: version 2"
$ git push
To https://github.com/YOUR-USERNAME/tutorial-monorepo.git
da9cef2..a99b162 main -> main
PS> git commit -am "web: version 2"
PS> git push
To https://github.com/YOUR-USERNAME/tutorial-monorepo.git
da9cef2..a99b162 main -> main
Your output shows your own commit ids. Only the web app redeployed: its Deployments tab has a new entry by GitHub Auto-Deploy , 14 seconds in my run:
Now change the API's version in server.js from const VERSION = 1; to const VERSION = 2;, commit with the message api: version 2 and push the same way. This time only the API rebuilt, and the web app's history did not change. The API's own history shows its two deployments and nothing from the web commit:
Refresh the web page: it shows "version": 2 from the API, with no new build of its own.
One more push to prove the rule: add a line to README.md, commit and push. The README is outside both folders, so nothing deployed at all; GitHub shows no check on that commit. Light Cloud compares the files in the pushed commits with each app's Root directory and skips the apps that are not affected.
Step 6: Preview a branch
Branches behave a little differently from pushes to main. Create one and change the heading in apps/web/src/App.jsx from One repository, two apps to One repository, many apps:
$ git switch -c new-heading
$ git commit -am "web: new heading"
$ git push -u origin new-heading
remote: Create a pull request for 'new-heading' on GitHub by visiting:
remote: https://github.com/YOUR-USERNAME/tutorial-monorepo/pull/new/new-heading
To https://github.com/YOUR-USERNAME/tutorial-monorepo.git
* [new branch] new-heading -> new-heading
branch 'new-heading' set up to track 'origin/new-heading'.
PS> git switch -c new-heading
PS> git commit -am "web: new heading"
PS> git push -u origin new-heading
remote: Create a pull request for 'new-heading' on GitHub by visiting:
remote: https://github.com/YOUR-USERNAME/tutorial-monorepo/pull/new/new-heading
To https://github.com/YOUR-USERNAME/tutorial-monorepo.git
* [new branch] new-heading -> new-heading
branch 'new-heading' set up to track 'origin/new-heading'.
A new branch gets a preview environment for every app on the repository, not only for the app whose folder changed. Both apps now list New Heading next to Production ; the web preview was answering 25 seconds after the push, the API preview after a minute and a half, because its container was built again:
Open the link GitHub printed and click Create pull request. Light Cloud comments once per app, each with its own preview address:
The preview environments start with each app's own variables. So the web preview still has VITE_API_URL pointing at the production API, and that is what it shows:
If a preview must talk to its own API preview instead, open the web preview environment's Settings and give VITE_API_URL the API preview's address; the site rebuilds with it.
Merge the branch and delete it:
$ git switch main
$ git merge new-heading
Updating d82f329..8c35173
Fast-forward
apps/web/src/App.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push
$ git push origin --delete new-heading
PS> git switch main
PS> git merge new-heading
Updating d82f329..8c35173
Fast-forward
apps/web/src/App.jsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
PS> git push
PS> git push origin --delete new-heading
The merge is a push to main that touches only apps/web, so only the web app redeployed (17 seconds). Deleting the branch removed both preview environments: the API preview was gone 6 seconds later, the web preview after a minute, and both comments on the pull request changed to Preview removed.
Change the folder later
An app's folder is not fixed at creation. Open the app (not an environment), click Settings , and change Root Directory. The new folder is used from the next deployment on:
Light Cloud names things after the app once a repository has more than one: the GitHub checks read Light Cloud - monorepo-web / Production and Light Cloud - monorepo-api / Production , and so do the pull request comments, so you can always tell which app a result belongs to.
Troubleshooting
We're not sure what this is
The repository root has no project files Light Cloud recognises, which is normal for a monorepo. Set Root directory to the app's folder; detection runs again inside it.
Could not reach the api: VITE_API_URL is not set
The web app was built without the variable. Add VITE_API_URL in the environment's Settings and let it redeploy: a static site reads variables when it builds, not when a visitor opens it.
A push redeployed nothing
Only apps whose Root directory contains a changed file redeploy. Check which folder the commit touched; a change to the repository's README or to another app's folder does not deploy this one.
















Top comments (0)