royvoetman/flysystem-gitlab-storage
A Flysystem adapter for storing files in GitLab repositories via the Repository Files API v4. Configure it with a project ID, branch, base URL, and personal access token, plus an optional path prefix. Works with Flysystem 3 and includes a Laravel integration.
Architecture Fit
royvoetman/flysystem-gitlab-storage is a thin League\Flysystem\FilesystemAdapter that translates Flysystem operations into GitLab Repository Files API v4 calls. For a Laravel application this is the correct architectural seam: application code talks to Storage::disk(...), the disk resolves to a Flysystem Filesystem, and the GitLab adapter is a swappable implementation detail configured in config/filesystems.php. No domain code should ever reference GitLab.
The decisive architectural fact is that this is a Git repository, not an object store. Every write is a commit on the configured branch. Directory creation is emulated with .gitkeep files (v1.0.0). Listing is a paginated tree walk (v1.1.0). There is no native file-level locking, no multipart upload path, and — despite common assumptions — no evidence of Git LFS support in the README or changelog; assume large files land in the repository proper until proven otherwise. That makes this adapter a good fit for low-churn, version-controlled artifacts and static exports, and a poor fit for user uploads, caches, session data, or anything written per-request. "Use GitLab as our storage backend" is a product decision with second-order effects on repository size, clone times, CI runner performance, and backup strategy — not merely a config change.
Integration Feasibility
royvoetman/laravel-gitlab-storage. Two dependency surfaces for one capability, each with its own release cadence and maintainer attention.Storage::extend('gitlab', ...) around the adapter. Estimate 1–2 days including tests. Do not let the bridge be a gate.env() values and a 12-factor secrets story.branch is the effective "bucket" analogue. That is a genuinely useful isolation primitive (per-environment or per-tenant branches) and a live footgun if it defaults to main in production.Technical Risk
| Risk | Severity | Notes / Mitigation |
|---|---|---|
| Single maintainer, 11 stars, 0 dependents | High | No community backstop. Budget for a vendor fork and pin. |
| Commit-per-write semantics | High | Repository bloat degrades every developer clone, CI checkout, and IDE index. Isolate to a dedicated project/branch. |
| PAT blast radius | High | A repo-write token is a broad credential. Use a dedicated service account, least-privilege scope, secrets manager, rotation runbook. |
| Rate limiting (429) | Medium | Batch writes; exponential backoff with jitter; cache listings aggressively. |
| Concurrent write conflicts | Medium | No locking; parallel writes produce conflicting commits. Assume last-write-wins plus retry — never transactional guarantees. |
| Laravel bridge currency | Medium | Verify Flysystem 3 and current Laravel support before committing. |
| Flysystem major churn | Medium | v1/v2 users are stranded on 1.1.0 / 2.0.4. Only v3 aligns with Laravel 9+. |
| Path encoding regression | Low | v3.1.1 replaced urlencode() with rawurlencode() because current GitLab rejects + for spaces. Hard floor: ^3.1.1. |
| No LFS / large-file story | Medium | Confirm empirically; treat multi-MB files as out of scope. |
Key Questions
copy/move natively or emulate them as read-then-write? This drives both latency and failure modes.Stack Fit
config/filesystems.php; consumption via the Storage facade keeps the application backend-agnostic and preserves future portability.path/prefix constructor argument maps naturally to per-environment or per-tenant scoping inside one repository — use it deliberately rather than sharing a root.Migration Path
Storage.Compatibility
^3.1.1 at minimum and add a regression test asserting that paths containing spaces, +, #, and non-ASCII characters round-trip. This bug class is exactly what a low-star package will not catch for us.Sequencing
Maintenance
The package is effectively a maintained-by-courtesy project: one author, 11 stars, zero dependents, and a release cadence that tracks upstream fixes as one-line patches (v3.1.1 encoding, v3.1.2 PHP 8.4 nullable deprecation). That last one is instructive — the adapter went roughly 14 months between the 2024 and 2025 releases, and the 2026 entry exists only because PHP changed. Plan for a fork. Realistically this means vendoring the adapter, running our own CI and static analysis, and accepting that Flysystem 4 will require us to port it ourselves. Budget roughly 2–4 days per year of baseline upkeep, plus an unfunded migration whenever Flysystem makes a major move.
Support
There is no vendor and no SLA. Issues on the tracker may go unanswered. Internal ownership must be assigned to a named team before adoption; adoption without a named owner is how this becomes an orphaned dependency discovered during an incident. Escalation path is "we fix it" — which is acceptable for a ~10-class adapter but must be stated in the ADR.
Scaling
Scaling is the weakest dimension and it scales along the wrong axis. Throughput is bounded by GitLab API rate limits and by Git's own serialization of commits onto a branch, not by our application tier. Listing cost grows with repository size because tree walks paginate. Storage grows monotonically — the adapter has no meaningful delete history story, so "deleted" files persist in history and continue inflating clones forever. Concurrency is unsafe by construction. The correct scaling strategy is horizontal isolation: one project or branch per environment or tenant, with explicit size and object-count budgets and alerts when they are approached.
Failure Modes
Ramp-Up
Very low for consumers — anyone who knows Laravel's Storage facade is productive in minutes, and no GitLab API knowledge is required. The real ramp-up cost sits with the platform team: understanding GitLab's Repository Files API, pagination, rate limits, token scoping, and Git's commit semantics well enough to debug a 409 or a 429 at 2am. Estimate 2–3 days of focused onboarding for the owning engineer, most of it on GitLab API behaviour rather than on this package. Onboarding documentation should be written in-house during the pilot — the README covers installation and construction, but says nothing about failure behaviour, concurrency, or size limits, which is precisely what an on-call engineer will need.
How can I help you explore Laravel packages today?