Custom Apps

How to Deploy an App Without a Git Provider

Some code should not sit on someone else's platform waiting to be scanned, indexed or subpoenaed. Upload the archive instead and deploy the exact bytes you sent.

Deploying from GitHub or GitLab means your source lives on a company that knows who you are, keeps its own copies, and answers to its own jurisdiction. For plenty of projects that is fine. For the projects people bring to an offshore host, it is often the whole problem.

You can skip it. Upload the project as an archive, and the platform keeps that exact version, with its own identity and checksum, as the source for your builds.

What you get

  • No third party repository: the source goes from your machine to your server
  • An immutable version: every upload gets its own identifier, SHA256 and size
  • Reuse without re-uploading: rebuild from a retained version, or select a different one
  • Verified on arrival: the agent checks size and checksum and validates extraction before building
  • Stored privately, outside any public web root

Two ways to upload

From the portal. Open My Apps, then Source uploads, and select a .tar.gz or .tgz archive with your project files at the archive root. Label the version if you want to recognize it later. The portal sends your archive unchanged.

From your terminal or your AI agent. With the local MCP connector, upload a folder directly and it is packed for you. The hosted connector can list, select and delete uploads, but it cannot read files on your computer, which is the correct boundary rather than a limitation.

The packer is not a secret scanner

Local packing excludes the usual dependency and version control folders, .env, .env.* other than examples, .npmrc and .pypirc. It does not read your .gitignore or .dockerignore, and it does not look for credentials. Review the archive yourself before uploading. Runtime secrets belong in the app’s environment settings, never in retained source.

Deploy it

Prepare the archive

Put the project at the archive root, including whatever your recipe needs: the Dockerfile, or package.json with its lockfile, or requirements.txt, or composer.json with composer.lock. Remove local dependency folders and anything you would not hand to a server.

Upload the version

Through the portal or the local connector. You get back an identifier, a SHA256 and a size. Nothing is deployed yet.

Inspect it and save a plan

Before creating the app, inspect the uploaded project. The plan records the source version, its checksum, the selected folder, the findings and the build options available for it. Choose one, save the server and runtime configuration for review, then confirm.

Create the app

Choose the uploaded archive as the source, pick the build strategy and configure the server, name, port and domain as usual.

Rebuild from a version

On the app card, expand Source versions. Reuse the selected archive or choose another and confirm the redeploy. The app identity, domain, host port and build recipe stay fixed.

What a version identifier actually fixes

It fixes the bytes you uploaded. It does not freeze a mutable base image tag, the dependencies downloaded during the build, external data or runtime secrets. Two builds of the same archive can still differ if something they pull from the internet changed. Lockfiles are what narrow that gap, which is why every generated recipe insists on one.

Retention, and the deadline that surprises people

A retained source stays available while an application references it, including an app that is queued or failed. Once nothing references it, it expires seven days after the upload or the last deployment request.

The part worth reading twice: that deadline is not reset when an app switches to another version or is uninstalled. An older version that quietly lost its last reference can become eligible for cleanup immediately. The sweep removes the file and its metadata, and expired files cannot be recovered.

Defaults are 10 unexpired or referenced uploads and 300 MB per account, with at most 100 MB per archive. Referenced sources count toward that. Delete versions you no longer need on purpose, rather than letting the quota decide for you. An archive an app still references cannot be deleted at all.

If you do use Git, pin the commit instead of the branch

There is a middle path. Inspection also accepts a repository URL with a complete 40 character commit, over public HTTPS on port 443, and captures that exact tree as a retained archive. Preparing and applying the plan use those captured bytes, not a moving branch and not a second fetch. A token supplied for a private fetch is used for the fetch only and is not saved in the plan or the deployment. Redirects, credentials in the URL and submodules are refused, and nothing in your project is executed during inspection.

Where it stops

Changing the build recipe of an existing uploaded source app requires a new app: a new archive is rebuilt with the strategy, paths, public build variables, limits and volumes the app already has. Uploads also do not replace Compose topology import, and they are not image promotion between environments.

A failed build preserves the previous running release where supported. Failure after replacement follows your startup and recovery policy, described in recovering from a failed deployment.

Start now

Get an offshore VPS with the agent preselected, pick the recipe that matches your project (Node, Python, PHP or static), and sign up without an email address.

Ready to build privacy-first?

No KYC, no email required, crypto payment. Deploy an offshore server in minutes, or do it all by chat with the Impreza agent.