Weblate Automated VCS Synchronization: A Least-Privilege Architecture Note
How to design Weblate's automated VCS synchronization with per-project credentials, background workers, and clear trust boundaries between the translation database and source repositories.
08 Aug 2025, 06:34 UTC

The problem is the credential, not the protocol
Keeping translation files synchronized with a source repository sounds like a solved problem until the synchronization service holds write credentials, runs outside the request/response cycle, and can block every translator when a network partition leaves a stale lock. The useful takeaway is that Weblate's VCS synchronization should be architected as a least-privilege, asynchronous bridge—not as a broad integration.
Requirements
Before choosing a design, separate the hard requirements from conveniences:
- Bidirectional flow: Weblate must pull upstream changes (new source strings, external commits) and push completed translations back without manual file copying.
- Non-blocking operations: Git network calls must not run inside web requests; they belong in background workers so translators are not stalled by remote latency.
- Controlled writes: Only components explicitly configured for push should write to a repository, and ideally only to translation file paths.
- Recoverable state: Transient outages must not leave a component permanently locked or silently drop translation commits.
Smallest suitable design
The smallest design that satisfies those requirements has four parts:
- A dedicated VCS service account per project (or per repository). Do not reuse a personal account or an organization-wide bot. The account exists only to let Weblate read and write translation files.
- Per-component repository configuration. Each component declares its own repository URL, push branch, and file mask (for example,
*/locale/*.po). This granularity prevents one project's credentials from reaching another repository. - Background workers. Pull, commit, and push cycles run asynchronously—typically through Celery workers in a standard Weblate deployment—so the web interface stays responsive.
- Encrypted transport. All remote access uses HTTPS or SSH. Plain-text transport is unacceptable because the channel carries both credentials and translation content.
This design does not require webhooks. Many self-hosted instances rely on periodic polling or manual triggers, which is acceptable when the repository is not reachable from the internet.
Trust and data boundaries
The boundary between Weblate's database and the VCS remote is a privileged channel. Weblate stores credentials that grant write access to source repositories; if that boundary is compromised, an attacker can modify code, not just translations.
- Scope credentials tightly. Generate a token or SSH key limited to the specific repositories Weblate manages. Avoid organization-wide scopes that would let a compromised Weblate instance push to unrelated projects.
- Restrict push paths where possible. If your VCS host supports branch protection or path-based permissions, limit the service account to translation directories. This reduces the blast radius if Weblate is misconfigured.
- Treat the database as sensitive. Because credentials live there, database backups and replicas inherit the same trust requirements as the VCS tokens themselves.
Operational checks
Synchronization fails quietly unless you instrument it. At minimum, monitor:
| Signal | What to check | Why it matters |
|---|---|---|
| Component repository status | Last successful pull/push timestamp | Detects stalled sync before translators notice |
| Repository lock age | How long a component has been locked | Crashed workers can leave locks that block updates |
| Background queue depth | Pending pull/commit/push jobs | Rising depth indicates workers are saturated or failing |
| Commit metadata | Author name/email on pushed commits | Confirms pushes originate from Weblate, not a leaked token |
| Authentication errors | Repeated 401/403 or SSH failures | Expired or revoked credentials cause silent drop-off |
Check the administrative interface or logs for repository health indicators. In a healthy deployment, commit timestamps and push logs should align with actual translation activity.
Concrete example: staging conflict test
A common failure is an external commit that touches the same translation file Weblate is editing. To verify your deployment handles this, simulate it in staging.
# Run in a local staging clone of the repository configured in Weblate.
# Requires: write access to the staging repo and git installed.
git checkout <push-branch>
printf '\n# external change\n' >> locale/<lang>/LC_MESSAGES/django.po
git commit -am "Simulate external commit for conflict testing"
git push origin <push-branch>
Expected check: Weblate should either merge the external change automatically or mark the component with a merge conflict state visible in the administrative interface. If the component remains locked without explanation, the failure mode is not recoverable without manual intervention.
Risk: Running this against production creates a real conflict and may block translation pushes. Use a disposable staging repository only.
Failure modes
Four failure modes account for most operational pain:
- Network partitions or VCS outages: A remote that becomes unreachable mid-operation can leave a component locked or in an inconsistent state. Workers should retry with backoff, but locks may need manual clearing.
- Merge conflicts from concurrent external commits: If a developer edits a
.pofile directly in the repository while Weblate holds changes, the automated push will fail. The component must surface a conflict state rather than overwrite upstream work. - Expired or revoked credentials: Some VCS providers rotate tokens automatically. Weblate may continue to accept translation changes locally while failing to push, creating a silent divergence between the database and the repository.
- Crashed workers: A worker killed mid-push can leave a repository lock. Without lock expiry or manual cleanup, subsequent updates for that component are blocked.
Conditions that would change the design
The minimal design above fits many teams, but three conditions justify changes:
- Monorepo structures: When translations for multiple products live in one repository, per-repository components are too coarse. Switch to path-based component filtering so each product gets its own file mask and push scope within the shared repo.
- Air-gapped or restricted networks: If the VCS host cannot receive webhooks, replace event-driven triggers with short-interval polling or manual update buttons. Accept higher latency in exchange for not exposing internal services.
- High-concurrency teams: When many translators and developers commit simultaneously, default locking may be too coarse. Consider sharded repository workers (dedicating worker processes to specific components) or stricter lock timeouts to reduce contention.
Limitations and a practical check
Weblate's repository locking is a safety mechanism, not a guarantee. After a hard crash, a lock may persist until an administrator clears it or a timeout expires. Webhook-based push triggers also assume network reachability that not every deployment has.
A practical verification path for a new deployment:
- Configure one component against a staging repository with a repo-scoped service account.
- Make a translation change in the web interface and confirm a commit appears with the expected author metadata.
- Revoke the service account token and confirm Weblate surfaces a clear repository error rather than silently accepting further changes.
- Check the administrative interface or logs to confirm background workers process pending commits without timing out.
If those four checks pass, the synchronization path is behaving as designed. If any fail, fix the boundary before adding more components.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.