How Did We Handle Python Dependencies Without PyPI in a Secure FinTech Environment?
While working on a core transaction processing engine for a heavily regulated FinTech platform, our engineering team encountered a situation where standard development practices collided with strict enterprise security protocols. Our developers were building the application in Python, utilizing standard tools and defining dependencies in pyproject.toml to fetch packages from PyPI. However, the production deployment environments—specifically targeting Red Hat Enterprise Linux (RHEL) and Rocky Linux—were entirely air-gapped. For security and compliance reasons, the customer strictly prohibited fetching packages from external sources like PyPI.
Instead, the customer required that all production software, including our Python application’s dependencies, be installed exclusively via approved, native Linux distribution repositories. This meant we needed a way to maintain PyPI dependency resolution for our developers while specifying OS-level package versions for the deployment environments. Striking this balance is a common architectural hurdle and this challenge inspired the article so others can avoid the same mistake of mixing package management paradigms. For organizations looking to hire software developer talent, having engineers who understand the nuances of system-level versus application-level dependency management is crucial.
Why Do Air-Gapped Environments Require Native Linux Repository Dependencies?
In high-security industries like banking, healthcare and defense, infrastructure teams enforce strict supply chain security. Relying on PyPI at deployment time introduces external variables: unexpected package updates, potential typosquatting attacks or network availability issues. By mandating that dependencies come from RHEL or Rocky Linux native repositories via tools like dnf or yum, operations teams can guarantee that all packages have been tested, signed and approved by the OS vendor or internal security teams.
The core business use case was to deliver our Python application as a seamless, compliant deployment artifact. The architectural concern surfaced when we realized that Python’s standardized dependency management (PEP 621) explicitly governs the Python ecosystem, not the underlying operating system’s package manager. We needed a strategy that allowed our remote engineering teams to work efficiently without requiring them to manually install RPM packages during local development.
Where Did Standard Python Dependency Management Fall Short?
The issue surfaced during our initial staging deployments. Our CI/CD pipeline attempted to run pip install . on the target Rocky Linux machine, which immediately timed out due to firewall blocks. The initial thought was to use environment markers in the pyproject.toml file.
However, we quickly realized a fundamental architectural oversight: environment markers in pyproject.toml only instruct pip on when to install a PyPI package based on the platform. They do not tell the system to bypass pip entirely and use dnf instead. Furthermore, the naming conventions between PyPI and Linux repositories differ significantly. A PyPI package named requests is typically packaged as python3-requests in RHEL/Rocky repositories. The symptoms of this oversight were broken staging builds and frustrating bottlenecks as we tried to force a Python-specific tool to behave like a system-level configuration manager.
What Strategies Did We Consider for Dual-Dependency Management?
To solve this, our engineering team evaluated several approaches. We knew we had to maintain a clean development experience, which is exactly why clients hire python developers for enterprise modernization through WeblineGlobal—to ensure structured, scalable solutions rather than hacky workarounds.
Did Environment Markers in pyproject.toml Work?
We first considered utilizing PEP 508 environment markers. While powerful for specifying different dependencies for Windows versus Linux, this approach failed our primary requirement. Markers still rely on PyPI for resolution. There is no standard way within pyproject.toml to map a Python dependency to an RPM package name or delegate installation to a native Linux package manager.
Could We Use Separate Requirements Files?
Another option was to maintain a traditional requirements.txt for developers and a separate requirements-rhel.txt or shell script for production. We quickly discarded this. Maintaining parallel lists of dependencies manually almost always leads to configuration drift, where developers add a package to one file and forget the other, causing late-stage deployment failures.
Was Modifying setup.py a Viable Option?
In older Python projects, teams sometimes added custom installation commands within setup.py to trigger OS-level commands. This is now considered an anti-pattern. Modern Python packaging has moved away from executable setup.py files in favor of declarative configurations in pyproject.toml. Reverting to legacy practices would compromise the long-term maintainability of the project.
Why Did We Choose an RPM Spec Based Approach?
We concluded that the most robust solution was strict separation of concerns. pyproject.toml would remain the single source of truth for the Python ecosystem (development). For deployment, we would treat our Python application not as a Python package, but as a native Linux RPM package. By utilizing an RPM .spec file, we could map the PyPI dependencies to their corresponding Linux package names and automate the translation via our CI/CD pipeline.
How Did We Implement the RPM and PyPI Dual Strategy?
Our final implementation decoupled the development environment from the deployment artifact. We retained standard tooling for developers while outputting a compliant Linux package for the customer.
First, we defined our standard developer dependencies in pyproject.toml:
[project]
name = "enterprise-tx-engine"
version = "1.2.0"
dependencies = [
"requests>=2.28.0",
"pydantic>=1.10.0",
"SQLAlchemy>=1.4.0"
]
Next, we created an RPM spec file template (enterprise-tx-engine.spec) that lived in our repository. In this file, we specified the Linux-native equivalents of our Python dependencies using the Requires directive. This is a best practice implemented when companies hire devops engineers for secure infrastructure management.
Name: enterprise-tx-engine
Version: 1.2.0
Release: 1%{?dist}
Summary: Core transaction processing engine
License: Proprietary
BuildRequires: python3-devel
Requires: python3-requests >= 2.28.0
Requires: python3-pydantic >= 1.10.0
Requires: python3-sqlalchemy >= 1.4.0
%description
Enterprise transaction engine packaged for secure RHEL/Rocky deployment.
%install
# CI/CD instructions to copy Python source files to /opt/enterprise-tx-engine/
To prevent configuration drift, we implemented a validation step in our CI pipeline. A custom script parsed the pyproject.toml dependencies and cross-referenced them against the Requires lines in the .spec file. If a developer added a new PyPI package without updating the corresponding RPM dependency in the .spec file, the build would fail locally before a pull request could be merged.
What Should Engineering Teams Learn About Secure Dependency Management?
Based on this deployment, here are the actionable insights engineering teams should apply when handling hybrid dependency models:
- Respect Separation of Concerns: Do not force language-level package managers (like Pip or Poetry) to manage OS-level constraints. Use the right tool for the environment.
- Package for the Target OS: If the target environment blocks PyPI, ship your Python application as a native package (RPM or DEB). It integrates perfectly with enterprise security scanners and deployment tools.
- Automate Dependency Synchronization: If you must maintain two dependency definitions (e.g., PyPI and RPM Spec), enforce synchronization strictly via CI/CD linting. Never rely on developers to manually update both.
- Understand Naming Conventions: Be aware that PyPI package names rarely match OS repository names exactly. Maintain an internal mapping logic or use tools like
pyp2rpmfor automation where feasible. - Avoid Legacy Workarounds: Resist the urge to use legacy
setup.pyscripting to run shell commands. Stick to declarative PEP 621 standards.
How Can You Streamline Secure Python Deployments?
Deploying software into highly secure, air-gapped environments requires moving beyond standard developer workflows. By leveraging pyproject.toml for PyPI-driven development and RPM spec files for native Linux deployments, our team delivered a robust, compliant solution without sacrificing developer velocity. Architectural foresight is critical when navigating strict infrastructure rules. If your team is facing complex deployment constraints and you need seasoned remote engineering talent, contact us to see how WeblineGlobal can support your next enterprise initiative.
Social Hashtags
#Python #PythonDevelopment #DevOps #Linux #RHEL #RockyLinux #RPM #PyPI #PythonPackaging #DependencyManagement #AirGapped #CyberSecurity #SoftwareSupplyChain #DevSecOps #EnterpriseSoftware #FinTech #CICD #SecureSoftware #SoftwareArchitecture #CloudSecurity
Frequently Asked Questions
No. pyproject.toml is designed strictly for the Python packaging ecosystem. It does not possess native capabilities to interact with OS-level package managers like yum, dnf or apt.
Linux distributions prepend python3- to package names (e.g., python3-requests) to distinguish them from standard OS utilities or libraries written in other languages, ensuring clean namespaces within their repositories.
Yes. You can use tools like pip download to pull .whl files and bundle them, then install them offline. However, strictly regulated environments often reject this approach because those wheels are not cryptographically signed by the OS vendor.
Yes, tools like pyp2rpm can automatically generate RPM spec files from Python packages. However, for complex enterprise applications, maintaining a custom .spec file alongside your code offers finer control over deployment paths and permissions.
If your secure environment allows containers, you can install the RPM packages within a Dockerfile using RUN dnf install -y python3-requests. The principle remains the same: bypass pip and rely entirely on the base image's secure Linux repositories.
Success Stories That Inspire
See how our team takes complex business challenges and turns them into powerful, scalable digital solutions. From custom software and web applications to automation, integrations, and cloud-ready systems, each project reflects our commitment to innovation, performance, and long-term value.

US SaaS Platform Cut Manual Ops by 70% After Hiring WeblineGlobal’s n8n Automation Pod

California-based SMB Hired Dedicated Developers to Build a Photography SaaS Platform

















