<!--
Suggested title:    Your Repo Lives on One Disk. That's One Spilled Coffee From Gone.
Alt title:          A Remote Is Just a Remote (and Why a Working Team Backs Itself Up by Accident)
Slug:               the-workflow-remotes-and-hosting
Meta description:   Pushing to a remote gets your Git history off your laptop and somewhere
                    durable. GitHub is the default, not the only option, and because every
                    clone carries full history, a working team stumbles into 3-2-1 backup
                    just by working.
Tags:               AI, developer workflow, Git, GitHub, self-hosting, backup, version control
-->

# Your Repo Lives on One Disk. That's One Spilled Coffee From Gone.

I run my own Git forge. Not GitHub; an actual server I keep at `git.jpaul.io`, behind my own Cloudflare, with my own runners and my own container registry on the LAN. Most of my projects live there first and only get pushed out to GitHub when I deliberately want them public.

I'm telling you that up front not to flex, but because this post is the one where I'm most in my own wheelhouse, and I want you to know the punchline before I prove it: **it does not matter where you push.** GitHub, GitLab, a box in my closet: the commands are identical, and the reason they're identical is the whole lesson.

This post opens Unit 2 of [The Workflow](https://git.jpaul.io/justin/ai-workflow-course), the team layer. Up to now the course has been about getting *you* and your AI working safely on one machine: version control as undo, the AI editing real files, your config committed as a durable artifact. All of that lives on one disk. This module gets it *off* that disk. If you've been following along, this is the moment the safety net stops being local.

## A remote is just another copy

Strip the branding away and a **remote** is one thing: a named pointer to *another copy of this same repository*, usually somewhere you can reach over the network. That's the entire concept.

Here's the part people miss because the marketing buries it. `origin` (the name you'll see everywhere) is not a GitHub thing. It's not a GitLab thing or a Gitea thing. It's a *Git* thing, and the copy it points at is a full, equal Git repo that just happens to live on a server. Which means `git push` to GitHub is byte-for-byte the same operation as `git push` to the forge I run myself in a locked-down rack. The provider is a logistics decision (uptime, price, who can see it, where the servers physically sit), not a Git decision.

That's why I keep saying it doesn't matter where you push. The vocabulary is small, and it's the same everywhere:

```bash
git remote add origin <URL>   # register a remote named "origin" (once per repo)
git remote -v                 # list remotes and their URLs
git push -u origin main       # send "main" up; -u links local main to origin/main
git push                      # after that first -u push, this is all you need
git pull                      # fetch the remote's changes AND merge them in
git fetch                     # fetch WITHOUT merging (look before you leap)
git clone <URL>               # make a brand-new local copy, full history and all
```

`origin` is just the conventional name for "the place I push to." You can have more than one: a personal fork *and* the team's repo, one on a SaaS forge and one on a box on your LAN. Git genuinely does not care.

## Getting a remote (and the three walls you'll hit first)

The one thing those commands assume is that a remote repo *exists* to push into. On every host the shape is identical: in the web UI, create a **new, empty** repository; do **not** let it add a README, license, or `.gitignore`, because you want your local history to be the first thing that lands in it. Copy the URL it hands you (HTTPS or SSH), then:

```bash
cd ~/ai-workflow-course/tasks-app
git remote add origin <URL-you-copied>
git push -u origin main
```

That `-u` is worth understanding rather than just copying; it records that your local `main` *tracks* `origin/main`, so afterward `git status` can tell you "your branch is ahead of origin/main by 2 commits," and bare `git push`/`git pull` know where to go.

[insert a screenshot referencing a host's "create new repository" page with the README/license/gitignore checkboxes left unchecked here]

Now, the first push is where everybody trips. I've watched sharp people lose an afternoon to one of these three, so let me just name them by their error text:

1. **Authentication fails:** `Authentication failed` or `Permission denied (publickey)`. You almost certainly tried an account password (dead on every modern host) or haven't set up a token / SSH key yet. Fix: generate a personal access token and use it as your password for HTTPS, or `ssh-keygen` and paste the public half into the host's settings for SSH. Host-specific UI, identical concept everywhere.
2. **The remote isn't empty:** `! [rejected] ... (fetch first)` or `non-fast-forward`. You let the host create the repo *with* a README, so it has a commit your history doesn't, and Git refuses to clobber it. Fix: recreate it empty, or reconcile once with `git pull --rebase origin main` and push.
3. **Branch-name mismatch:** `src refspec main does not match any`. Your local default is `master` but you're pushing `main`. Fix: check with `git branch`, then push what you actually have or rename it (`git branch -m main`).

Recognizing these by sight is the actual skill. The fix is always thirty seconds; the staring-at-it is the hour.

## The everyday loop

Once the remote exists, day-to-day work adds exactly two moves to the loop you already know: `git pull` before you start (grab whatever the remote gained), `git push` after you commit (send your checkpoints up).

And one habit I want you to keep: when you want to *see* what's incoming before it touches your files, use `git fetch` instead of `pull`. It downloads the remote's commits into `origin/main` but leaves your branch alone, so you can read exactly what's coming:

```bash
git fetch                 # download, don't merge
git log main..origin/main # SEE what's incoming
git pull                  # now take it
```

That "look before you leap" rhythm matters more the second other contributors (human *or* agent) are pushing to the same place.

## Choosing a host: GitHub is the default, not the only

GitHub is the titan, and I'm not going to pretend otherwise. It's the largest forge by a wide margin, it's where most open source lives, and (this is the part that matters for *this* course) it's where AI tooling integrates *first*. New coding agent ships? GitHub support is usually in the first release; everyone else trails. That makes it the sane default, which is why the course uses it as the worked example.

But "default" isn't "only," and if you're in this audience, you know exactly why. On-prem requirements. Air-gapped networks. Data-residency rules that make "someone else's hardware" a non-starter. The genuine choice is **hosted** (someone runs the forge, you just use it) versus **self-hosted** (you run it). On the hosted side you've got GitLab, Bitbucket, Azure DevOps, Codeberg, SourceHut. On the self-hosted side, the open-source forges: Forgejo and Gitea (a single Go binary that'll run happily on a 256 MB VPS, which is what I run), GitLab CE (heavy; wants 8 GB+ RAM and a whole stack to feed), Gogs, OneDev.

Two things to take away rather than memorize a price sheet that'll be stale by the time you read it:

- **GitLab spans both camps:** hosted SaaS *and* a self-hostable Community Edition from the same project. Handy if you want SaaS now and the *option* to bring it in-house later without changing tools.
- **Self-hosting trades a per-user bill for an ops bill.** The license is free; your cost is the server, the upgrades, the backups, the on-call. Forgejo/Gitea make that bill tiny. GitLab CE makes it real. That trade *is* the decision.

I'll say from experience: running my own forge is genuinely not the burden people assume. Gitea is one binary. It's been less maintenance than half the SaaS subscriptions I've juggled. But it *is* an ops commitment, and I'd be lying if I told you the backups and upgrades maintain themselves; they don't, and that's the honest cost.

## The backup thesis, part one: distribution *is* the backup

Here's the reframe I most want you to walk away with.

A single local repo gives you **recovery**: you can move between checkpoints, undo the AI's mess, time-travel through your own history. What it does *not* give you is **backup**. Drop the laptop in a lake and the repo, history and all, is gone. Recovery and backup are different powers, and one local repo only has the first one.

Pushing to a remote closes that gap, and Git's design makes the win bigger than it looks. Recall the standard **3-2-1 rule**: keep **3** copies of your data, on **2** different media, with **1** offsite. Now watch what a normal team ends up with *without anyone running a backup tool*:

- Your laptop has a full copy: complete history, not just current files.
- The remote has a full copy, offsite, on different hardware.
- Every teammate who's cloned the repo has *another* full copy, each with the entire history, because **`clone` copies everything**, not a snapshot.

A four-person team pushing to one remote is sitting on five-plus complete, independent copies of the whole project history, across multiple machines and locations. They didn't *do* backups. They just worked. That's the quiet superpower of a *distributed* version control system: distribution is the redundancy. The thing most ops shops fight to satisfy deliberately falls out of a forge and a working team almost for free.

You can watch it happen with your own eyes in the lab. Push your `tasks-app`, then clone it into a separate directory as if you were a teammate on a fresh machine, and count the commits in each:

```bash
cd ~/ai-workflow-course
git clone <URL> tasks-app-teammate
cd tasks-app-teammate
git log --oneline | wc -l    # compare to your original repo; they match
```

The clone didn't get "the current files." It got the whole project's memory. That's the property that turns a working team into an accidental backup system.

[insert a screenshot referencing two terminal panes side by side, each running `git log --oneline | wc -l` and showing the same commit count here]

But be precise about who does what, because the course is honest about where analogies stop:

- **Recovery power comes from commits.** Point-in-time restore. Go back to any checkpoint.
- **Backup power comes from remotes and distribution.** Offsite, redundant, survives-the-dead-drive copies.

You need both. Commits without a remote survive a mistake but not a dead drive. A remote without good commits survives a dead drive but gives you a junk drawer to restore from.

## The AI angle

A remote isn't only about durability; it's the substrate the AI half of this course runs on.

Most AI tooling operates on the *remote*, not your laptop. AI reviewers, issue-to-PR agents, the CI that catches code which merely *looks* right: all of it acts on the pushed repo through its API and web UI. Until your history is up there, none of that machinery has anything to grab onto. A remote is the precondition for every agent-in-the-loop module that follows.

And the AI config you committed earlier in the course? Locally it just configures *your* agent. Pushed, it configures *everyone's*: every teammate who clones, and every automated agent that later runs on the repo, inherits the same conventions instead of each drifting into a private setup. The remote is what turns "my AI config" into "the project's AI config."

One more, and it's the one I care most about: **a remote is an agent's recovery insurance.** When you hand an agent a branch and let it run, a *pushed* branch means its work survives a crashed session, a wiped worktree, or a machine that dies mid-run. An agent's output that exists only in one uncommitted, unpushed working directory is the single most fragile state in this whole course. Push early.

## Where it breaks (because I like to be honest)

The backup analogy especially needs its caveats, so here they are:

- **A remote backs up what you *pushed*, nothing else.** Uncommitted edits, untracked files, and anything `.gitignore` excludes never leave your laptop. "I pushed" means "every committed-and-pushed change is safe," not "everything is safe." The defense is the habit: commit often, and now push often too.
- **Git is not a backup for non-Git things.** Your database, your secrets (which shouldn't be in the repo anyway), large binaries, build artifacts: pushing code does not cover any of them. The 3-2-1-by-accident win applies to your *versioned source*, full stop.
- **One remote is one vendor.** Distribution across a team is great redundancy against *disk* failure; it's weaker against *account* failure. If your whole team only ever pushes to one host and that account gets suspended or the provider has an outage, your offsite copy is temporarily out of reach (your local clones are fine). A second remote (a fork on another host, a bare repo on a USB drive, a box on your LAN) is the answer for anyone who needs it. This, by the way, is the on-ramp to the whole self-hosting argument, and it's a big part of why I run my own forge in the first place.
- **"GitHub integrates first" is true today and a moving target.** Don't treat the AI-ecosystem gap between hosts as permanent; it's exactly the kind of claim that ages. Re-check it for your tooling before you let it pick your host.

## You're done when

Your `tasks-app` exists on a remote: `git remote -v` and the host's web page both confirm it. You've pushed at least one commit and pulled one back across two copies of the repo. And you can explain, in your own words, why a four-person team pushing to one remote roughly satisfies 3-2-1 without running a backup tool, and name two things that win doesn't cover.

When pushing feels like the natural end of "commit," and you trust that your history is no longer trapped on one disk, you've got the *backup* half of the backup-and-recovery thread. The course comes back later to finish the *recovery* half, and it's just as blunt about what Git is **not** a backup for.

Next up in the series: now that the repo lives somewhere shared, we start using the remote for more than storage: the issue layer, where humans and agents pick up work.

Running your own forge, or thinking about it? Tell me what's holding you back in the comments; I read them, and the on-prem/air-gapped war stories are exactly the ones I want to hear.
