Newbie Question: Why Do We Need to Request Apps on Cloudron?
-
Hello everyone,
I’d like to ask something about the app request process on Cloudron.Why do we actually need to submit a request for an application to be added to Cloudron’s app installation list?
Is it possible to add custom applications to Cloudron? If yes or no, is it something difficult for beginners or is there another reason behind it?
This is a genuine newbie question, and I don’t have an IT background. I hope someone can shed some light on this for me.

Thank you!
Best regards. -
The "request app" Wishlist is for apps to get official Cloudron Dev Team support. Meaning, that the app will be available in the Cloudron App Store and all updates, etc are managed and provided automatically from the Cloudron team.
To get your own (or other 3rd party) apps run in Cloudron you can follow the packaging tutorial: https://docs.cloudron.io/packaging/tutorial/
-
Hello @IniBudi
@IniBudi said in Newbie Question: Why Do We Need to Request Apps on Cloudron?:
This is a genuine newbie question, and I don’t have an IT background. I hope someone can shed some light on this for me.
I will go into a bit of detail here, so be prepared for a wall of text.
@IniBudi said in Newbie Question: Why Do We Need to Request Apps on Cloudron?:
Why do we actually need to submit a request for an application to be added to Cloudron’s app installation list?
Every app in the Cloudron App-Store is pre-vetted by the Cloudron team and comes with life-cycle testing before publishing updates to all the users of Cloudron to ensure as minimal friction as possible when using Cloudron and the apps available.
This means, every app needs to be understood on an application and technical level by the Cloudron team and handled accordingly so the end-user of Cloudron has as minimal as possible issues with the apps.
As you can imagine, these life-cycle tests don't just materialize out of thin air.
From the understanding of the packaged application, the life-cycle tests are developed to ensure a seamless app update process.
Let's take the Nextcloud app.
For the Nextcloud app for each app update runs through the life-cycle test https://git.cloudron.io/packages/nextcloud-app/-/blob/master/test/test.js which needs to succeed before the app update is shipped to you the end-user.// no sso it('install app (NO SSO)', function () { execSync(`cloudron install --no-sso --location ${LOCATION}`, EXEC_ARGS); }); it('can get app information', getAppInfo); it('can login as admin', login.bind(null, adminUser, adminPassword)); it('can close wizard', closeWizard); it('can upload file', uploadFile.bind(null, adminUser, adminPassword)); it('can check file', checkFile.bind(null, 'sticker')); it('can download previously uploaded file', testFileDownload.bind(null, adminUser, adminPassword)); it('can logout', logout); it('uninstall app', async function () { await browser.get('about:blank'); // ensure we don't hit NXDOMAIN in the mean time execSync(`cloudron uninstall --app ${app.id}`, EXEC_ARGS); }); // sso it('install app (SSO)', function () { execSync(`cloudron install --location ${LOCATION}`, EXEC_ARGS); }); it('can get app information', getAppInfo); it('can login OIDC', loginOIDC.bind(null, username, password)); it('can close the wizard', closeWizard); it('can logout', logout); it('can login as admin', login.bind(null, adminUser, adminPassword)); it('can close wizard', closeWizard); it('can upload file', uploadFile.bind(null, adminUser, adminPassword)); it('can check file', checkFile.bind(null, 'sticker')); it('can download previously uploaded file', testFileDownload.bind(null, adminUser, adminPassword)); xit('can list users', listUsers); xit('has no setup warnings', checkSetupWarnings); it('can logout', logout); it('can restart app', function () { execSync(`cloudron restart --app ${app.id}`); }); it('can login OIDC', loginOIDC.bind(null, username, password)); it('can check file', checkFile.bind(null, 'Readme')); it('can logout', logout); it('can admin login', login.bind(null, adminUser, adminPassword)); it('can check file', checkFile.bind(null, 'sticker')); it('can download previously uploaded file', testFileDownload.bind(null, adminUser, adminPassword)); xit('can list users', listUsers); it('can logout', logout); it('backup app', function () { execSync(`cloudron backup create --app ${app.id}`); }); it('restore app', function () { const backups = JSON.parse(execSync(`cloudron backup list --raw --app ${app.id}`)); execSync('cloudron uninstall --app ' + app.id, EXEC_ARGS); execSync('cloudron install --location ' + LOCATION, EXEC_ARGS); getAppInfo(); execSync(`cloudron restore --backup ${backups[0].id} --app ${app.id}`, EXEC_ARGS); }); it('can login OIDC', loginOIDC.bind(null, username, password)); it('can check file', checkFile.bind(null, 'Readme')); it('can logout', logout); it('can admin login', login.bind(null, adminUser, adminPassword)); it('can check file', checkFile.bind(null, 'sticker')); it('can download previously uploaded file', testFileDownload.bind(null, adminUser, adminPassword)); xit('can list users', listUsers); xit('has no setup warnings', checkSetupWarnings); it('can logout', logout); it('move to different location', async function () { browser.manage().deleteAllCookies(); await browser.get('about:blank'); // ensure we don't hit NXDOMAIN in the mean time execSync(`cloudron configure --app ${app.id} --location ${LOCATION}2`, EXEC_ARGS); getAppInfo(); }); it('can login OIDC', loginOIDC.bind(null, username, password)); it('can check file', checkFile.bind(null, 'Readme')); it('can logout', logout); it('can admin login', login.bind(null, adminUser, adminPassword)); it('can check file', checkFile.bind(null, 'sticker')); it('can download previously uploaded file', testFileDownload.bind(null, adminUser, adminPassword)); xit('can list users', listUsers); xit('has no setup warnings', checkSetupWarnings); it('uninstall app', async function () { await browser.get('about:blank'); // ensure we don't hit NXDOMAIN in the mean time execSync(`cloudron uninstall --app ${app.id}`, EXEC_ARGS); }); // test update it('can install app for update', function () { execSync(`cloudron install --appstore-id com.nextcloud.cloudronapp --location ${LOCATION}`, EXEC_ARGS); }); it('can get app information', getAppInfo); it('can admin login', login.bind(null, adminUser, adminPassword)); it('can close the wizard', closeWizard); it('can upload file', uploadFile.bind(null, adminUser, adminPassword)); it('can logout', logout); it('can update', function () { execSync(`cloudron update --no-backup --app ${LOCATION}`, EXEC_ARGS); }); it('can login OIDC', loginOIDC.bind(null, username, password)); it('can close the wizard', closeWizard); it('can check file', checkFile.bind(null, 'Readme')); it('can logout', logout); it('can admin login', login.bind(null, adminUser, adminPassword)); xit('can close the wizard', closeWizard); it('can check file', checkFile.bind(null, 'sticker')); it('can download previously uploaded file', testFileDownload.bind(null, adminUser, adminPassword)); xit('has no setup warnings', checkSetupWarnings); // it('can logout', logout); it('uninstall app', async function () { await browser.get('about:blank'); // ensure we don't hit NXDOMAIN in the mean time execSync(`cloudron uninstall --app ${app.id}`, EXEC_ARGS); });And this could still be considered a very broad life-cycle test.
Since Nextcloud has the capability to be unpredictably on the end-user part.
You can install 562 "apps" / "plugins" for Nextcloud which in turn would need their own life-cycle test again.
But since each of these "plugins" again is custom code from somewhere and in an unknown multiplication with other "plugins" makes it impossible to predict and thus test.
The more complex the app and more extendable the app itself, the harder it gets to maintain and the Cloudron team needs to find a good balance for each app.This all needs to be handled by the Cloudron team so the end-user, you, can simply install and update an app without worrying.
From this angle it might be more understandable that simply adding a new app to the app-store is not actually that simple.
@IniBudi said in Newbie Question: Why Do We Need to Request Apps on Cloudron?:
Is it possible to add custom applications to Cloudron? If yes or no, is it something difficult for beginners or is there another reason behind it?
Possible, yes.
Difficult? Depends.
The user @timconsidine developed a custom app installer https://forum.cloudron.io/topic/14231/ccai-cloudron-custom-app-installer available at https://ccai.appx.uk/ which makes the installation of already existing custom Cloudron apps easier for "newbie" users.already existing custom Cloudron apps?
From my previous explanation about the technicality of Cloudron apps and just the life-cycle tests.
Each Cloudron app needs to follow the Cloudron app packaging rules.
A small glimpse into the rules that can make it difficult to package an application for Cloudron.
All source-code most exist in/app/codeand only backup worthy data (user-data) should be stored in/app/data
/app/codeis read-only to ensure the security, see https://docs.cloudron.io/security/#app-isolation-and-sandboxingApps run with a read-only rootfs preventing attacks where the application code can be tampered with.
Meaning, when packaging an app for Cloudron, the packaging developer needs to understand the application he is packaging to certain degree to ensure this.
The more complex the app and more extendable the app itself, the more difficult it can be to package and maintain the app.
Broadly spoken, you can't just take the source-code of e.g. Agno - python-based open-source alternative to n8n and just make it an app for Cloudron.I know that @BrutalBirdie started the Community Event - Workshop / Webinar - App Packaging initiative, which might be something for people like you.