{"id":496,"date":"2025-05-22T23:41:35","date_gmt":"2025-05-22T21:41:35","guid":{"rendered":"https:\/\/camilomatajira.wordpress.com\/?p=496"},"modified":"2025-09-29T18:40:35","modified_gmt":"2025-09-29T18:40:35","slug":"create-debian-package-with-virtual-environment-and-python-interpreter-inside-using-uv","status":"publish","type":"post","link":"https:\/\/camilo.matajira.com\/?p=496","title":{"rendered":"Create Debian package with virtual environment and Python interpreter inside using UV"},"content":{"rendered":"\n<h1 class=\"wp-block-heading\">Goals:<\/h1>\n\n\n\n<p>Create a Debian package that contains both the virtual environment and the Python interpreter inside.<\/p>\n\n\n\n<p>Why is this project useful? Because you may need to deploy a Debian package and you would like control over the Python interpreter and over your dependencies. Control equals autonomy. Autonomy is needed to getting things done.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Alternatives:<\/h1>\n\n\n\n<p>The packaging of Python applications is a world in itself. For a summary read <a href=\"https:\/\/packaging.python.org\/en\/latest\/overview\/\">here<\/a>: <br>There are of course similar and established projects that tackle the problem that I am trying to solve.<\/p>\n\n\n\n<p>Probably the closest project to this one is <a href=\"https:\/\/dh-virtualenv.readthedocs.io\/en\/1.2.1\/\">dh_virtualenv<\/a> .<br>dh_virtualenv allows you to create Python debian packages with the virtual environment inside.<br>It was a project created by Spotify, and for a long time has been among my favorite tools.<br>What I like is that it works well with the setup.py, and the executables that setup.py creates &#8211;they point to the correct python interpreter (the one inside the virtual environment).<\/p>\n\n\n\n<p>I would like to continue using dh_virtualenv, however I would also like to add the Python interpreter inside the package, and I would like native support for UV. For example, I would like a tool that uses directly the uv.lock instead of me having to compile the uv.lock to a requirements.txt, so that it could be understood by dh_virtualenv.<\/p>\n\n\n\n<p>Also, there is <a href=\"https:\/\/pyinstaller.org\/en\/stable\/\">pyinstaller<\/a>, which actually bundles your code, virtual environment and python interpreter into a single executable. A workflow based on pyinstaller would be to create the virtual environment with uv (including the library pyinstaller), then execute pyinstaller to create the bundled binary (actually a zip file), and finally create the debian package with the bundled binary inside.<br>Pyinstaller supports several versions of python upto 3.13. Other similar projects are <a href=\"https:\/\/nuitka.net\/\">Nuitka<\/a> an <a href=\"https:\/\/pyoxidizer.readthedocs.io\/en\/stable\/\">PyOxidizer<\/a>. These two project produce a single binary from the python project. Nuitka supports Python 3 (3.4 \u2014 3.13) and Python 2 (2.6, 2.7), while PyOxydizer only supports Python 3.8, 3.9, and 3.10.<\/p>\n\n\n\n<p>The downside of Pyinstaller, Nuitka and Pyoxidizer is that despite being interesting projects, in the case of a web development app they are not very useful: There are no clear benefits of &#8220;compiling&#8221; the code (I am not exactly sure that the code is actually compiled) because the webservers (e.g uwsgi, gunicorn, etc.) work with text files. Hence, despite that there is some merit on distributing your whole app in a single binary the risk of adding an unnecessary project to your pipeline may not be worth it.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">The solution<\/h1>\n\n\n\n<p>I splitted this project in two parts. In the first, I had to learn how to insert a Python interpreter (installed via uv) inside a Debian package.<br>You can read that project <a href=\"https:\/\/camilomatajira.wordpress.com\/2025\/05\/19\/create-debian-package-out-of-a-python-installed-via-uv\/\">here<\/a>: <br>You can clonee the first project <a href=\"https:\/\/gitlab.com\/matajira\/debian-only-uv-python\">here<\/a>:<\/p>\n\n\n\n<p>The second part builds upon the first project, but in this case its not only the python interpreter, but the virtual environment and the project code.<br>You can clone the project <a href=\"https:\/\/gitlab.com\/matajira\/debian-uv-python#\">here<\/a>:<\/p>\n\n\n\n<p>Basically we have a small fastapi app that we want to package in a debian format, with the virtual environment and the python interpret inside:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#272822\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><textarea class=\"code-block-pro-copy-button-textarea\" aria-hidden=\"true\" readonly>import sys\nfrom fastapi import FastAPI\napp = FastAPI()\n@app.get(&#8220;\/&#8221;)\nasync def root():\n    print(sys.version)\n    return {&#8220;message&#8221;: f&#8221;Hello World using python {sys.version}&#8221;}<\/textarea><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki monokai\" style=\"background-color: #272822\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F92672\">import<\/span><span style=\"color: #F8F8F2\"> sys<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F92672\">from<\/span><span style=\"color: #F8F8F2\"> fastapi <\/span><span style=\"color: #F92672\">import<\/span><span style=\"color: #F8F8F2\"> FastAPI<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">app <\/span><span style=\"color: #F92672\">=<\/span><span style=\"color: #F8F8F2\"> FastAPI()<\/span><\/span>\n<span class=\"line\"><span style=\"color: #A6E22E\">@app.get<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #E6DB74\">&quot;\/&quot;<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #66D9EF; font-style: italic\">async<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #66D9EF; font-style: italic\">def<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #A6E22E\">root<\/span><span style=\"color: #F8F8F2\">():<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #66D9EF\">print<\/span><span style=\"color: #F8F8F2\">(sys.version)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #F92672\">return<\/span><span style=\"color: #F8F8F2\"> {<\/span><span style=\"color: #E6DB74\">&quot;message&quot;<\/span><span style=\"color: #F8F8F2\">: <\/span><span style=\"color: #66D9EF; font-style: italic\">f<\/span><span style=\"color: #E6DB74\">&quot;Hello World using python <\/span><span style=\"color: #AE81FF\">{<\/span><span style=\"color: #F8F8F2\">sys.version<\/span><span style=\"color: #AE81FF\">}<\/span><span style=\"color: #E6DB74\">&quot;<\/span><span style=\"color: #F8F8F2\">}<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>The project dependencies are given in pyproject.toml<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#272822\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><textarea class=\"code-block-pro-copy-button-textarea\" aria-hidden=\"true\" readonly>[project]\nname = &#8220;debian-uv-python&#8221;\nversion = &#8220;0.1.0&#8221;\ndescription = &#8220;Add your description here&#8221;\nreadme = &#8220;README.md&#8221;\nrequires-python = &#8220;>=3.9&#8221;\ndependencies = [\n    &#8220;fastapi[standard]>=0.115.12&#8221;,\n]<\/textarea><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki monokai\" style=\"background-color: #272822\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F8F8F2\">[project]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">name <\/span><span style=\"color: #F92672\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E6DB74\">&quot;debian-uv-python&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">version <\/span><span style=\"color: #F92672\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E6DB74\">&quot;0.1.0&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">description <\/span><span style=\"color: #F92672\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E6DB74\">&quot;Add your description here&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">readme <\/span><span style=\"color: #F92672\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E6DB74\">&quot;README.md&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">requires<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">python <\/span><span style=\"color: #F92672\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E6DB74\">&quot;&gt;=3.9&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">dependencies <\/span><span style=\"color: #F92672\">=<\/span><span style=\"color: #F8F8F2\"> [<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #E6DB74\">&quot;fastapi[standard]&gt;=0.115.12&quot;<\/span><span style=\"color: #F8F8F2\">,<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">]<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>We also pinned the python version to be 3.9<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#272822\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><textarea class=\"code-block-pro-copy-button-textarea\" aria-hidden=\"true\" readonly>cat .\/.python-version\n3.9<\/textarea><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki monokai\" style=\"background-color: #272822\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F8F8F2\">cat .<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">.python<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">version<\/span><\/span>\n<span class=\"line\"><span style=\"color: #AE81FF\">3.9<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>And here is the makefile that provides the solution:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#272822\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><textarea class=\"code-block-pro-copy-button-textarea\" aria-hidden=\"true\" readonly>PREFIX = \/opt\/venvs\/debianuvpython\/\nSHELL = \/bin\/bash\n.ONESHELL:\ndefault: help\ninstall:\n    set -eu\n    curl -LsSf https:\/\/astral.sh\/uv\/0.7.5\/install.sh | sh\n    source $$HOME\/.local\/bin\/env\n  # Declare venv &#8211;relocatable so that the executables in the venv do not have the python interpreter hardcoded\n    uv venv &#8211;relocatable\n    uv sync\n    # Get the path of the interpreter used\/created by UV\n    python_interpreter_dir_to_copy=$$(uv python list &#8211;only-installed | awk &#8216;\/debian\/ {gsub(\/bin\\\/python3.9\/, &#8220;&#8221;); print $$2}&#8217;)\n    name_of_python_interpreter_to_copy=$$(uv python list &#8211;only-installed | awk &#8216;\/debian\/ {gsub(\/bin\\\/python3.9\/, &#8220;&#8221;); print $$1}&#8217;)\n    mkdir -p $(DESTDIR)$(PREFIX)\n    cp -R $${python_interpreter_dir_to_copy} $(DESTDIR)$(PREFIX)\n    # Fix the dynamic libraries of the python interpreter\n    patchelf &#8211;replace-needed &#8220;\\$$ORIGIN\/..\/lib\/libpython3.9.so.1.0&#8221; &#8220;$(PREFIX)\/$${name_of_python_interpreter_to_copy}\/lib\/libpython3.9.so.1.0&#8221; $(DESTDIR)$(PREFIX)\/$${name_of_python_interpreter_to_copy}\/bin\/python3\n    patchelf &#8211;replace-needed &#8220;\\$$ORIGIN\/..\/lib\/libpython3.9.so.1.0&#8221; &#8220;$(PREFIX)\/$${name_of_python_interpreter_to_copy}\/lib\/libpython3.9.so.1.0&#8221; $(DESTDIR)$(PREFIX)\/$${name_of_python_interpreter_to_copy}\/lib\/libpython3.so\n    cp -R .\/.venv\/* $(DESTDIR)$(PREFIX)\n    # Fix the symbolic links\n    rm -f $(DESTDIR)$(PREFIX)\/bin\/python &amp;&amp; ln -s ..\/$${name_of_python_interpreter_to_copy}\/bin\/python $(DESTDIR)$(PREFIX)\/bin\/python\n  # Copy the actual code of the application\n    cp -R .\/example_fastapi $(DESTDIR)$(PREFIX)\nclean:\n    rm -rf .\/.venv\n    rm -rf .\/debian\/debianuvpython\n    rm -rf .\/debian\/.debhelper\nDOCKER_RUN := docker run  -v$$PWD:\/home\/debian-only-uv-python-1.0.0 debian:bullseye\nbuild:\n    $(DOCKER_RUN) bash -c &#8220;apt-get update &amp;&amp; apt-get install -y devscripts debmake build-essential patchelf &amp;&amp; cd \/home\/debian-only-uv-python-1.0.0 &amp;&amp; debmake -y &#8211;native; cat debian\/rules | awk &#8216;\/override_dh_shlibdeps:\/ {exit 0} END {exit 1}&#8217; || echo -e &#8216;override_dh_shlibdeps:\\n\\tdh_shlibdeps -Xcpython-3.9.22-linux-x86_64-gnu&#8217; >> debian\/rules; debuild &amp;&amp; cp ..\/*deb .\/&#8221;\nhelp:\n    echo &#8220;This is debian loves uv&#8221;<\/textarea><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki monokai\" style=\"background-color: #272822\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #AE81FF\">PREFIX<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F92672\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">opt<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">venvs<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">debianuvpython<\/span><span style=\"color: #F92672\">\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #AE81FF\">SHELL<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F92672\">=<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #66D9EF\">bin<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">bash<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">.<\/span><span style=\"color: #AE81FF\">ONESHELL<\/span><span style=\"color: #F8F8F2\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">default: <\/span><span style=\"color: #66D9EF\">help<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">install:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #66D9EF; font-style: italic\">set<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">eu<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    curl <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">LsSf https:<\/span><span style=\"color: #F92672\">\/\/<\/span><span style=\"color: #F8F8F2\">astral.sh<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">uv<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #AE81FF\">0.7<\/span><span style=\"color: #F8F8F2\">.5<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">install.sh <\/span><span style=\"color: #F92672\">|<\/span><span style=\"color: #F8F8F2\"> sh<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    source <\/span><span style=\"color: #F44747\">$$<\/span><span style=\"color: #AE81FF\">HOME<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">.local<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #66D9EF\">bin<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">env<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  <\/span><span style=\"color: #88846F\"># Declare venv --relocatable so that the executables in the venv do not have the python interpreter hardcoded<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    uv venv <\/span><span style=\"color: #F44747\">--<\/span><span style=\"color: #F8F8F2\">relocatable<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    uv sync<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #88846F\"># Get the path of the interpreter used\/created by UV<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    python_interpreter_dir_to_copy<\/span><span style=\"color: #F92672\">=<\/span><span style=\"color: #F44747\">$$<\/span><span style=\"color: #F8F8F2\">(uv python <\/span><span style=\"color: #66D9EF; font-style: italic\">list<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F44747\">--<\/span><span style=\"color: #F8F8F2\">only<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">installed <\/span><span style=\"color: #F92672\">|<\/span><span style=\"color: #F8F8F2\"> awk <\/span><span style=\"color: #E6DB74\">&#39;\/debian\/ {gsub(\/bin\\\/python3.9\/, &quot;&quot;); print $$2}&#39;<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    name_of_python_interpreter_to_copy<\/span><span style=\"color: #F92672\">=<\/span><span style=\"color: #F44747\">$$<\/span><span style=\"color: #F8F8F2\">(uv python <\/span><span style=\"color: #66D9EF; font-style: italic\">list<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F44747\">--<\/span><span style=\"color: #F8F8F2\">only<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">installed <\/span><span style=\"color: #F92672\">|<\/span><span style=\"color: #F8F8F2\"> awk <\/span><span style=\"color: #E6DB74\">&#39;\/debian\/ {gsub(\/bin\\\/python3.9\/, &quot;&quot;); print $$1}&#39;<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    mkdir <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">p <\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">DESTDIR<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">PREFIX<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    cp <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">R <\/span><span style=\"color: #F44747\">$$<\/span><span style=\"color: #F8F8F2\">{python_interpreter_dir_to_copy} <\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">DESTDIR<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">PREFIX<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #88846F\"># Fix the dynamic libraries of the python interpreter<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    patchelf <\/span><span style=\"color: #F44747\">--<\/span><span style=\"color: #F8F8F2\">replace<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">needed <\/span><span style=\"color: #E6DB74\">&quot;\\$$ORIGIN\/..\/lib\/libpython3.9.so.1.0&quot;<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E6DB74\">&quot;$(PREFIX)\/$$<\/span><span style=\"color: #AE81FF\">{name_of_python_interpreter_to_copy}<\/span><span style=\"color: #E6DB74\">\/lib\/libpython3.9.so.1.0&quot;<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">DESTDIR<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">PREFIX<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F44747\">$$<\/span><span style=\"color: #F8F8F2\">{name_of_python_interpreter_to_copy}<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #66D9EF\">bin<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">python3<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    patchelf <\/span><span style=\"color: #F44747\">--<\/span><span style=\"color: #F8F8F2\">replace<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">needed <\/span><span style=\"color: #E6DB74\">&quot;\\$$ORIGIN\/..\/lib\/libpython3.9.so.1.0&quot;<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #E6DB74\">&quot;$(PREFIX)\/$$<\/span><span style=\"color: #AE81FF\">{name_of_python_interpreter_to_copy}<\/span><span style=\"color: #E6DB74\">\/lib\/libpython3.9.so.1.0&quot;<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">DESTDIR<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">PREFIX<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F44747\">$$<\/span><span style=\"color: #F8F8F2\">{name_of_python_interpreter_to_copy}<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">lib<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">libpython3.so<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    cp <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">R .<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">.venv<\/span><span style=\"color: #F92672\">\/*<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">DESTDIR<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">PREFIX<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #88846F\"># Fix the symbolic links<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    rm <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">f <\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">DESTDIR<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">PREFIX<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #66D9EF\">bin<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">python <\/span><span style=\"color: #F44747\">&amp;&amp;<\/span><span style=\"color: #F8F8F2\"> ln <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">s ..<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F44747\">$$<\/span><span style=\"color: #F8F8F2\">{name_of_python_interpreter_to_copy}<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #66D9EF\">bin<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">python <\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">DESTDIR<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">PREFIX<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #66D9EF\">bin<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">python<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">  <\/span><span style=\"color: #88846F\"># Copy the actual code of the application<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    cp <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">R .<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">example_fastapi <\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">DESTDIR<\/span><span style=\"color: #F8F8F2\">)<\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">PREFIX<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">clean:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    rm <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">rf .<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">.venv<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    rm <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">rf .<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">debian<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">debianuvpython<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    rm <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">rf .<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">debian<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">.debhelper<\/span><\/span>\n<span class=\"line\"><span style=\"color: #AE81FF\">DOCKER_RUN<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F92672\">:=<\/span><span style=\"color: #F8F8F2\"> docker run  <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">v<\/span><span style=\"color: #F44747\">$$<\/span><span style=\"color: #AE81FF\">PWD<\/span><span style=\"color: #F8F8F2\">:<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">home<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">debian<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">only<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">uv<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">python<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #AE81FF\">1.0<\/span><span style=\"color: #F8F8F2\">.0 debian:bullseye<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">build:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    <\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">DOCKER_RUN<\/span><span style=\"color: #F8F8F2\">) bash <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">c <\/span><span style=\"color: #E6DB74\">&quot;apt-get update &amp;&amp; apt-get install -y devscripts debmake build-essential patchelf &amp;&amp; cd \/home\/debian-only-uv-python-1.0.0 &amp;&amp; debmake -y --native; cat debian\/rules | awk &#39;\/override_dh_shlibdeps:\/ {exit 0} END {exit 1}&#39; || echo -e &#39;override_dh_shlibdeps:<\/span><span style=\"color: #AE81FF\">\\n\\t<\/span><span style=\"color: #E6DB74\">dh_shlibdeps -Xcpython-3.9.22-linux-x86_64-gnu&#39; &gt;&gt; debian\/rules; debuild &amp;&amp; cp ..\/*deb .\/&quot;<\/span><\/span>\n<span class=\"line\"><span style=\"color: #66D9EF\">help<\/span><span style=\"color: #F8F8F2\">:<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">    echo <\/span><span style=\"color: #E6DB74\">&quot;This is debian loves uv&quot;<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>What this Makefile does is to leverage the dh_autoinstall debhelper. When dpkg sees a makefile, dh_autoinstall will execute make install as part of building the package. This gives your the space to put any low level logic there.<br>In my case I use for:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Install uv<\/li>\n\n\n\n<li>Create the virtual environment and install the python interpreter<\/li>\n\n\n\n<li>Patch the ELF executable of the Python interpreter<\/li>\n\n\n\n<li>Copy the application code and fix some symlinks<\/li>\n<\/ol>\n\n\n\n<p>Perhaps, what is key of this makefile (not mentioning the low level details discussed in the previous project https:\/\/camilomatajira.wordpress.com\/2025\/05\/19\/create-debian-package-out-of-a-python-installed-via-uv\/) is the need to use uv venv &#8211;relocatable<br>so that the executables of the virtual environment point to the correct python interpreter.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to build<\/h2>\n\n\n\n<p>I provide the following utility to help you build\/try the project.<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#272822\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><textarea class=\"code-block-pro-copy-button-textarea\" aria-hidden=\"true\" readonly>make build<\/textarea><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki monokai\" style=\"background-color: #272822\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F8F8F2\">make build<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<h1 class=\"wp-block-heading\">Results<\/h1>\n\n\n\n<p>Start by executing:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#272822\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><textarea class=\"code-block-pro-copy-button-textarea\" aria-hidden=\"true\" readonly>make build\n(&#8230;)\nW: debian-uv-python: unusual-interpreter opt\/venvs\/debianuvpython\/lib\/python3.9\/site-packages\/markdown_it\/cli\/parse.py #!python\nW: debian-uv-python: wrong-bug-number-in-closes #nnnn in the installed changelog (line 3)\nFinished running lintian.<\/textarea><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki monokai\" style=\"background-color: #272822\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F8F8F2\">make build<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">(<\/span><span style=\"color: #AE81FF\">...<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">W: debian<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">uv<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">python: unusual<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">interpreter opt<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">venvs<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">debianuvpython<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">lib<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">python3.9<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">site<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">packages<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">markdown_it<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">cli<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">parse.py <\/span><span style=\"color: #88846F\">#!python<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">W: debian<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">uv<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">python: wrong<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">bug<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">number<\/span><span style=\"color: #F92672\">-in-<\/span><span style=\"color: #F8F8F2\">closes <\/span><span style=\"color: #88846F\">#nnnn in the installed changelog (line 3)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">Finished running lintian.<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Then, let&#8217;s install it in a new container and execute uvicorn!<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#272822\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><textarea class=\"code-block-pro-copy-button-textarea\" aria-hidden=\"true\" readonly>docker run -it -v$PWD:\/home -p 8000:8000 &#8211;rm debian:bookworm  \/bin\/bash\nroot@a047cf292d66:\/# cd home\/\nroot@a047cf292d66:\/home# dpkg -i debian-uv-python_1.0.0_amd64.deb\nSelecting previously unselected package debian-uv-python.\n(Reading database &#8230; 6091 files and directories currently installed.)\nPreparing to unpack debian-uv-python_1.0.0_amd64.deb &#8230;\nUnpacking debian-uv-python (1.0.0) &#8230;\nSetting up debian-uv-python (1.0.0) &#8230;\nProcessing triggers for libc-bin (2.36-9+deb12u10) &#8230;\nroot@a047cf292d66:\/home# cd \/opt\/venvs\/debianuvpython\/example_fastapi\/\nroot@a047cf292d66:\/opt\/venvs\/debianuvpython\/example_fastapi# \/opt\/venvs\/debianuvpython\/bin\/uvicorn &#8211;host 0.0.0.0 main:app\nINFO:     Started server process [38]\nINFO:     Waiting for application startup.\nINFO:     Application startup complete.\nINFO:     Uvicorn running on http:\/\/0.0.0.0:8000 (Press CTRL+C to quit)<\/textarea><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki monokai\" style=\"background-color: #272822\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F8F8F2\">docker run <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">it <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">v<\/span><span style=\"color: #F44747\">$<\/span><span style=\"color: #AE81FF\">PWD<\/span><span style=\"color: #F8F8F2\">:<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">home <\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">p <\/span><span style=\"color: #AE81FF\">8000<\/span><span style=\"color: #F8F8F2\">:<\/span><span style=\"color: #AE81FF\">8000<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #F44747\">--<\/span><span style=\"color: #F8F8F2\">rm debian:bookworm  <\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #66D9EF\">bin<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">bash<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">root<\/span><span style=\"color: #F92672\">@<\/span><span style=\"color: #F8F8F2\">a047cf292d66:<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #88846F\"># cd home\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">root<\/span><span style=\"color: #F92672\">@<\/span><span style=\"color: #F8F8F2\">a047cf292d66:<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">home<\/span><span style=\"color: #88846F\"># dpkg -i debian-uv-python_1.0.0_amd64.deb<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">Selecting previously unselected package debian<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">uv<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">python.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">(Reading database <\/span><span style=\"color: #AE81FF\">...<\/span><span style=\"color: #F8F8F2\"> <\/span><span style=\"color: #AE81FF\">6091<\/span><span style=\"color: #F8F8F2\"> files <\/span><span style=\"color: #F92672\">and<\/span><span style=\"color: #F8F8F2\"> directories currently installed.)<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">Preparing to unpack debian<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">uv<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">python_1.0.0_amd64.deb <\/span><span style=\"color: #AE81FF\">...<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">Unpacking debian<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">uv<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">python (<\/span><span style=\"color: #AE81FF\">1.0<\/span><span style=\"color: #F8F8F2\">.0) <\/span><span style=\"color: #AE81FF\">...<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">Setting up debian<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">uv<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #F8F8F2\">python (<\/span><span style=\"color: #AE81FF\">1.0<\/span><span style=\"color: #F8F8F2\">.0) <\/span><span style=\"color: #AE81FF\">...<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">Processing triggers <\/span><span style=\"color: #F92672\">for<\/span><span style=\"color: #F8F8F2\"> libc<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #66D9EF\">bin<\/span><span style=\"color: #F8F8F2\"> (<\/span><span style=\"color: #AE81FF\">2.36<\/span><span style=\"color: #F92672\">-<\/span><span style=\"color: #AE81FF\">9<\/span><span style=\"color: #F92672\">+<\/span><span style=\"color: #F8F8F2\">deb12u10) <\/span><span style=\"color: #AE81FF\">...<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">root<\/span><span style=\"color: #F92672\">@<\/span><span style=\"color: #F8F8F2\">a047cf292d66:<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">home<\/span><span style=\"color: #88846F\"># cd \/opt\/venvs\/debianuvpython\/example_fastapi\/<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">root<\/span><span style=\"color: #F92672\">@<\/span><span style=\"color: #F8F8F2\">a047cf292d66:<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">opt<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">venvs<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">debianuvpython<\/span><span style=\"color: #F92672\">\/<\/span><span style=\"color: #F8F8F2\">example_fastapi<\/span><span style=\"color: #88846F\"># \/opt\/venvs\/debianuvpython\/bin\/uvicorn --host 0.0.0.0 main:app<\/span><\/span>\n<span class=\"line\"><span style=\"color: #AE81FF\">INFO<\/span><span style=\"color: #F8F8F2\">:     Started server process [<\/span><span style=\"color: #AE81FF\">38<\/span><span style=\"color: #F8F8F2\">]<\/span><\/span>\n<span class=\"line\"><span style=\"color: #AE81FF\">INFO<\/span><span style=\"color: #F8F8F2\">:     Waiting <\/span><span style=\"color: #F92672\">for<\/span><span style=\"color: #F8F8F2\"> application startup.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #AE81FF\">INFO<\/span><span style=\"color: #F8F8F2\">:     Application startup complete.<\/span><\/span>\n<span class=\"line\"><span style=\"color: #AE81FF\">INFO<\/span><span style=\"color: #F8F8F2\">:     Uvicorn running on http:<\/span><span style=\"color: #F92672\">\/\/<\/span><span style=\"color: #AE81FF\">0.0<\/span><span style=\"color: #F8F8F2\">.0.0:<\/span><span style=\"color: #AE81FF\">8000<\/span><span style=\"color: #F8F8F2\"> (Press <\/span><span style=\"color: #AE81FF\">CTRL<\/span><span style=\"color: #F92672\">+<\/span><span style=\"color: #F8F8F2\">C to <\/span><span style=\"color: #66D9EF\">quit<\/span><span style=\"color: #F8F8F2\">)<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>Now in you local:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#272822\"><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"54\" height=\"14\" viewBox=\"0 0 54 14\"><g fill=\"none\" fill-rule=\"evenodd\" transform=\"translate(1 1)\"><circle cx=\"6\" cy=\"6\" r=\"6\" fill=\"#FF5F56\" stroke=\"#E0443E\" stroke-width=\".5\"><\/circle><circle cx=\"26\" cy=\"6\" r=\"6\" fill=\"#FFBD2E\" stroke=\"#DEA123\" stroke-width=\".5\"><\/circle><circle cx=\"46\" cy=\"6\" r=\"6\" fill=\"#27C93F\" stroke=\"#1AAB29\" stroke-width=\".5\"><\/circle><\/g><\/svg><\/span><span role=\"button\" tabindex=\"0\" style=\"color:#F8F8F2;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><textarea class=\"code-block-pro-copy-button-textarea\" aria-hidden=\"true\" readonly>curl http:\/\/localhost:8000\n{&#8220;message&#8221;:&#8221;Hello World using python 3.9.22 (main, Apr  9 2025, 04:03:41) \\n[Clang 20.1.0 ]&#8221;}%<\/textarea><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2\"><\/path><\/svg><\/span><pre class=\"shiki monokai\" style=\"background-color: #272822\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #F8F8F2\">curl http:<\/span><span style=\"color: #F92672\">\/\/<\/span><span style=\"color: #F8F8F2\">localhost:<\/span><span style=\"color: #AE81FF\">8000<\/span><\/span>\n<span class=\"line\"><span style=\"color: #F8F8F2\">{<\/span><span style=\"color: #E6DB74\">&quot;message&quot;<\/span><span style=\"color: #F8F8F2\">:<\/span><span style=\"color: #E6DB74\">&quot;Hello World using python 3.9.22 (main, Apr  9 2025, 04:03:41) <\/span><span style=\"color: #AE81FF\">\\n<\/span><span style=\"color: #E6DB74\">[Clang 20.1.0 ]&quot;<\/span><span style=\"color: #F8F8F2\">}<\/span><span style=\"color: #F92672\">%<\/span><\/span><\/code><\/pre><\/div>\n\n\n\n<p>You see, we get the response 3.9.22!<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Conclusion<\/h1>\n\n\n\n<p>We were able to create a Debian package, with the code of a simple fastapi app, with a specific python interpreter and its virtual environment.<br>Uvicorn, which we used as webserver, was installed using uv!<br>However, this project is still a little bit raw. For example I hardcoded several mentions of python3.9, there is still no support for the setup.py executables. Let&#8217;s wait for the next time!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Goals: Create a Debian package that contains both the virtual environment and the Python interpreter inside. Why is this project useful? Because you may need to deploy a Debian package and you would like control over the Python interpreter and over your dependencies. Control equals autonomy. Autonomy is needed to getting things done. Alternatives: The&#8230;<\/p>\n","protected":false},"author":2,"featured_media":558,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[25,34,43,55,75],"class_list":["post-496","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-projects","tag-debian","tag-fastapi","tag-linux","tag-python","tag-uv"],"_links":{"self":[{"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/posts\/496","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=496"}],"version-history":[{"count":2,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/posts\/496\/revisions"}],"predecessor-version":[{"id":517,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/posts\/496\/revisions\/517"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=\/wp\/v2\/media\/558"}],"wp:attachment":[{"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=496"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=496"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/camilo.matajira.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=496"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}