{"id":18501052,"url":"https://github.com/sshnaidm/virtainers","last_synced_at":"2025-07-24T07:34:05.282Z","repository":{"id":82494079,"uuid":"193366914","full_name":"sshnaidm/virtainers","owner":"sshnaidm","description":"Running virtual machines as containers","archived":false,"fork":false,"pushed_at":"2024-02-19T14:21:15.000Z","size":25,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-09T18:44:04.010Z","etag":null,"topics":["containerd","containerization","containers","docker","libvirt","podman","virtual-machine","virtual-machine-container","virtualization"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sshnaidm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-23T15:49:04.000Z","updated_at":"2024-03-05T15:40:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"84f1c08a-4dce-427b-befb-51a2f8916862","html_url":"https://github.com/sshnaidm/virtainers","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sshnaidm/virtainers","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sshnaidm%2Fvirtainers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sshnaidm%2Fvirtainers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sshnaidm%2Fvirtainers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sshnaidm%2Fvirtainers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sshnaidm","download_url":"https://codeload.github.com/sshnaidm/virtainers/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sshnaidm%2Fvirtainers/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266808556,"owners_count":23987450,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-24T02:00:09.469Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["containerd","containerization","containers","docker","libvirt","podman","virtual-machine","virtual-machine-container","virtualization"],"created_at":"2024-11-06T13:51:47.545Z","updated_at":"2025-07-24T07:34:05.251Z","avatar_url":"https://github.com/sshnaidm.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Virtainers\n\nVirtual machines within containers\n----------------------------------\n\nTL;DR - run CentOS 7 virtual machine as a container\n```bash\ndocker run --privileged -v ~/.ssh/id_rsa.pub:/tmp/id_rsa.pub:ro --name centos7 -d -t docker.io/virtainers/centos:7\nIP=$(docker inspect centos7 -f \"{{ .NetworkSettings.IPAddress }}\")\ndocker logs centos7 # use to see when virtual machine is up, usually about a minute\nssh centos@$IP\n# PROFIT! you're inside a virtual machine\n```\n\n# Table of Contents\n- [Virtainers](#virtainers)\n  - [Virtual machines within containers](#virtual-machines-within-containers)\n- [Table of Contents](#table-of-contents)\n  - [Intro to virtainers](#intro-to-virtainers)\n    - [Why and how](#why-and-how)\n    - [Which containers engines are supported - docker and podman](#which-containers-engines-are-supported---docker-and-podman)\n    - [Which virtual machines are provided?](#which-virtual-machines-are-provided)\n  - [Running VM inside a container locally](#running-vm-inside-a-container-locally)\n    - [Options to inject user data](#options-to-inject-user-data)\n    - [Tweaking and customizing virtual machine parameters](#tweaking-and-customizing-virtual-machine-parameters)\n    - [Console connection](#console-connection)\n    - [Prepared virtainers of specific distro](#prepared-virtainers-of-specific-distro)\n    - [Generic virtainer](#generic-virtainer)\n    - [Persistent data](#persistent-data)\n    - [Connections between virtainers on the same host](#connections-between-virtainers-on-the-same-host)\n    - [Note for running docker inside a virtainer](#note-for-running-docker-inside-a-virtainer)\n      - [For podman users](#for-podman-users)\n  - [How to run virtainer with IP from external network](#how-to-run-virtainer-with-ip-from-external-network)\n    - [Bridges and macvlan networks](#bridges-and-macvlan-networks)\n  - [Use cases](#use-cases)\n  - [What's next?](#whats-next)\n\n\n## Intro to virtainers\nWe have two main isolation ways - it's virtualization and containerization. But what we don't have, it's simplicity,\neasy distribution and orchestration of containers with possibilities to deploy different operation systems of\nvirtualization.\n\nVirtainers are coming to solve this problem and provide easy deployment of isolated environment - virtual machine inside\na container, which allows to run any OS and enjoy of all containerization advantages and tools.\n\n### Why and how\nFor example you have a bare Jenkins slave, which runs Ubuntu 12.04 and you need to test your application or deployment\nscripts on Fedora, and your deployment scripts includes starting a Docker service via systemd, running there containers\nand restarting services.\n\nThere are systemd enabled containers, but when it comes to deal with restarts of Docker service inside and running\ncontainers it turns to total mess with bunch of hacks and non obvious solutions. And what if we want to test something\nnetwork related? This complicates things even more.\n\nThat's why the solution could be running a \"virtainer\".\n\nActually virtainer requires from host the same what can require a libvirt service - enabled virtualization option. But\nyou don't need to deal with any virtualization framework, tools, service like Vagrant, VirtualBox, VMWare or\nlibvirt/KVM, just a container - docker or podman installed. It will allow you to run VM in one simple command without\ndealing with any dependencies, additional packages, repositories, etc etc. It also allows you to provide a configured VM\nto any of your team members or even to publish it. You can publish your changes to virtual machine and everyone who\nuse them with virtainer will have the exactly same VM as you. It's only matter of publishing one file, which includes\nall changes you made for the VM from its start.\n\n### Which containers engines are supported - docker and podman\nWe support two containers engines - it's docker and podman. Podman is supported with \"sudo\" - not rootless containers.\nUsually the difference comes when we set up a special networking for virtainers, podman doesn't have network related\ncommand options, so it's done differently. When running just virtainer on the host connected to internal network, you\ncan easily replace \"docker\" by \"sudo podman\" and have the same experience.\n\n### Which virtual machines are provided?\nCurrently we provide one generic virtainer that can pick up any cloud image you set for it and ready virtainers\nthat already include the image inside:\n- Fedora 29\n- Fedora 30\n- CentOS 7\n- Ubuntu 18.04\n\nOthers could be added easily.\n\n## Running VM inside a container locally\nWe need to run docker in privileged mode: ``--priveleged``, nothing more special is required. Also you need to add\n``-t`` option to add a terminal, so you can see logs of VM starting.\nSo if we run a virtual machine, we most likely would like to inject a specific data there, either SSH keys or some\nmore advanced pre-configuration of the host. We use cloud images for virtainers to run VM from, so it supports\n[cloud-init](https://cloud-init.io/). You can pass any preconfigured cloud-init file and virtainer will inject it to VM.\n\n### Options to inject user data\nThe options are as following:\n1. If you map your SSH key to ``/tmp/id_rsa.pub key`` like ``-v ~/.ssh/id_rsa.pub:/tmp/id_rsa.pub:ro`` then virtainer\nwill inject your SSH key into virtual machine and you can SSH to it with a default image user. Usually it's:\n- ``fedora`` - for all Fedora VMs\n- ``centos`` - for all CentOS VMs\n- ``cloud-user`` - for all RHEL VMs\n- ``ubuntu`` - for all Ubuntu VMs\nPlease find out username of your cloud image if you use a generic virtainer.\n2. If you have your user-data in [cloud-config](https://cloudinit.readthedocs.io/en/latest/topics/examples.html) format,\nthen mount it to ``/tmp/user-data`` inside the container, like ``-v /path/to/my/user-data:/tmp/user-data``, and\nvirtainer will pick it up.\n3. If you have your own meta-data file, you can mount it to ``/tmp/meta-data`` file inside a container, like\n``-v /path/to/my/meta-data:/tmp/meta-data`` and virtainer will pick it up. Otherwise it will be generated for you\nautomatically using parameteres ``instance-id: localimage-01`` and ``local-hostname: cloudimage``.\n4. If you have your own prepared cloud-init.iso file with required user-data and meta-data inside, just mount it to\n``/tmp/cloud_init.iso`` inside the container as ``-v /path/to/my/cloud_init.iso:/tmp/cloud_init.iso``.\n5. And finally, if you don't specify anything, container will run with password ``password``, you can enter it by SSH or\nconsole. (**Try not to use this option because of security risks!**)\n\n### Tweaking and customizing virtual machine parameters\nCurrently there are parameters which could be customized via environment variables:\n- RAM of virtual machine in MB - ``$RAM`` (default: `1024`)\n- Virtual machine name - ``$VM_NAME`` (default: `vm`)\n- Number of CPUs - ``$CPU`` (default: `1`)\n- OS variant - ``$OS_VARIANT`` (default: `rhel7`)\n- Internal path for diff image (see [Persistent data](#persistent-data) section) - ``$IMAGE_DIR`` (default: `/mounted`)\n- Internal path for cloud-init ISO disk - ``$CLOUD_INIT_DISK`` (default: `/cloud_init.iso`)\n\nFor running virtainer with 4GB RAM and 2 CPUs run as:\n```bash\ndocker run -e RAM=4096 -e CPU=2 --privileged -v ~/.ssh/id_rsa.pub:/tmp/id_rsa.pub:ro --name fedora29 -d -t docker.io/virtainers/fedora:29\n```\n\n### Console connection\nVirtual machine is running using libvirt/KVM, so you can also enter the console:\n``docker exec -it virtainer_name virsh console --force vm``.\n\n### Prepared virtainers of specific distro\nFor your convinience there are scripts in root dir of repo to run virtainers:\n- [run-centos7.sh](https://github.com/sshnaidm/virtainers/blob/master/run-centos7.sh)\n- [run-fedora29.sh](https://github.com/sshnaidm/virtainers/blob/master/run-fedora29.sh)\n- [run-fedora30.sh](https://github.com/sshnaidm/virtainers/blob/master/run-fedora30.sh)\n- [run-generic.sh](https://github.com/sshnaidm/virtainers/blob/master/run-generic.sh)\n- [run-ubuntu18.04.sh](https://github.com/sshnaidm/virtainers/blob/master/run-ubuntu18.04.sh)\n\nWhen each one starts the virtual machine according to its name.\n\n### Generic virtainer\n``run-generic.sh`` script will run a virtual machine with your image, just export IMAGE_URL (url of cloud image) or\nIMAGE (path to image): ``IMAGE=/path/to/image  ./run-generic.sh`` or ``IMAGE_URL=http://url/to/image ./run-generic.sh``.\n\n### Persistent data\nVirtainers use backing image as base image for running OS and all diff will be written to different file\n(``$IMAGE_DIR`` which is by default ``/mounted``). By mounting ``$IMAGE_DIR`` to a local path on your host you'll get\n``local_image.qcow2`` file with diff that you can use later to restore virtual machine state.\n\nFor example:\n```bash\ndocker run --privileged -v ~/.ssh/id_rsa.pub:/tmp/id_rsa.pub:ro -v /tmp/data_folder:/mounted -d -t --name fedora29 docker.io/virtainers/fedora:29\nIP=$(docker inspect fedora29 -f \"{{ .NetworkSettings.IPAddress }}\")\nssh fedora@$IP\n# now let's install some additional package for example\nsudo dnf install -y vim\n# when package is installed, exit\nexit\n# remove the containers completely\ndocker rm -f fedora29\n# Let's check we have diff image in our host\nls /tmp/data_folder\n# Now let's run a new container\ndocker run --privileged -v ~/.ssh/id_rsa.pub:/tmp/id_rsa.pub:ro -v /tmp/data_folder:/mounted -d -t --name new-fedora29 docker.io/virtainers/fedora:29\nIP=$(docker inspect new-fedora29 -f \"{{ .NetworkSettings.IPAddress }}\")\nssh fedora@$IP\n# Check that packge is installed\nrpm -qa | grep vim\n```\nLocal diff image will contain everything you did on virtual machine and is ready to be picked up when you run a new\nvirtainer. It could be easily distributed, relocated, etc. But important: it will work only if you have the same version\nof backing image!\n\n### Connections between virtainers on the same host\nNothing special is required. Virtainers can connect each to other as usual:\n```\nssh fedora@172.17.0.3\nping 172.17.0.2\n\nPING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.\n64 bytes from 172.17.0.2: icmp_seq=1 ttl=62 time=1.43 ms\n64 bytes from 172.17.0.2: icmp_seq=2 ttl=62 time=0.301 ms\n64 bytes from 172.17.0.2: icmp_seq=3 ttl=62 time=0.903 ms\n\n```\n\n### Note for running docker inside a virtainer\nIn case you want to run a docker inside a virtual machine of virtainer, you'll need to use a different network. The\nproblem appears when docker installs its default ``docker0`` interface with ``172.17.0.1/16``. While outer network of\nvirtainer is also from this subnet, if you run it by default. Packets to outer world won't go from virtual machine and\nnetwork connectivity will break. To prevent this, if you plan to run docker inside a virtainer, create a different\nnetwork for virtainers:\n```bash\ndocker network create -d bridge --subnet=172.28.100.0/24 --ip-range=172.28.100.0/24 --gateway=172.28.100.1 virtual\ndocker run --privileged --name fedora29 -d -t --network=virtual docker.io/virtainers/fedora:29\n```\nThat way you don't need to care about overlapping of docker networks inside and outside of virtainer. To discover an IP\nof container just run ``docker inspect fedora29 -f \"{{ .NetworkSettings.Networks.virtual.IPAddress }}\"`` where\n``virtual`` is your network name from previous step.\nYou also can play with IP ranges, for example if creating network like that:\n```bash\ndocker network create -d bridge --subnet=172.28.100.0/24 --ip-range=172.28.100.2/32 --gateway=172.28.100.1 feels_alone\n```\nYou will have only one posible IP which is 172.28.100.2, so you don't even need to inspect container for finding an IP.\n\nAnother way to run a container with well known IP is just to set it in command line:\n```bash\ndocker run --privileged -v ~/.ssh/id_rsa.pub:/tmp/id_rsa.pub:ro --name fefora -d -t --ip 172.28.100.10 --network=virtual docker.io/virtainers/fedora:29\n```\nPay attention that specifying IP address works with custom networks only, not for default one (``172.17.0.0/16``)\n\n#### For podman users\nThe same problem as docker has (see section above) exists in podman setup. Podman creates interface ``cni0`` with IP\naddress ``10.88.0.1/16`` and if you run podman in virtainer which ran by podman (not confusing at all) then networks\nwill overlap and connectivity will break. So we need to do the same as we did with docker above - to create on our host\na different network for podman. How to do it?\n\nPodman uses CNI networking and it's usually defined in ``/etc/cni/net.d``, for example in file\n``/etc/cni/net.d/87-podman-bridge.conflist``. Let's edit it:\n\n``sudo vim /etc/cni/net.d/87-podman-bridge.conflist``\n\nIf we want just change default network for podman, let's replace it with a new one (then we don't need to set network\noptions in podman command line). Replace ``cni0`` name of interface, which most likely already exists with ``cni1`` for\nexample. Also change network range from ``\"subnet\": \"10.89.0.0/16\"`` to something different like\n``\"subnet\": \"10.99.0.0/16\"``. After then make sure you don't run any containers in previous network:\n``sudo podman rm -f -a``. Then you can delete old ``cni0`` interface by: ``sudo ip link del cni0``. When you run podman\nnow, the new interface ``cni1`` will be created on the fly and container will have address from a new IP range. Voila!\n\n**Using a custom network is strongly recommended while using virtainers to prevent possible clashes and networks overlaps.**\n\n## How to run virtainer with IP from external network\nStraight through approach will be creating a container network with IP range of external network, then running a\ncontainer with assigning IP of this network. The only danger here is overlapping with main network and using same IP for\nmultiple hosts. Better to use a narrower prefix for IP subnet of container network and limit DHCP range of main network\nto avoid an overlapping.\nFor example your have `192.168.2.0/24` external DHCP network and you excluded addresses higher than `192.168.2.200` from\nDHCP. Then let's create a subnet `/27`:\n```bash\ndocker network create --ip-range 192.168.2.224/27 --attachable --gateway 192.168.2.225 --subnet 192.168.2.224/27 externalnet\n```\nYou'll get bridge interface on your host `192.168.2.225` which will be a gateway for virtainers from `192.168.2.224/27`\nnetwork.\n```bash\n$ ip a | grep 192.168.2.\ninet 192.168.2.160/24 brd 192.168.2.255 scope global dynamic noprefixroute eth0\ninet 192.168.2.225/27 scope global br-315eaa8b9537\n```\nNow we can run a virtainer in this network:\n```bash\ndocker run -d -t --name netcontainer --privileged -v ~/.ssh/id_rsa.pub:/tmp/id_rsa.pub:ro --network externalnet docker.io/virtainers/fedora:30\n```\nInside a container we'll have IP address from our range `192.168.2.224/27`:\n```bash\n/ $ ip add\n1: lo: \u003cLOOPBACK,UP,LOWER_UP\u003e mtu 65536 qdisc noqueue state UNKNOWN qlen 1000\n    ...\n150: eth0@if151: \u003cBROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN\u003e mtu 1500 qdisc noqueue state UP\n    link/ether 02:42:c0:a8:02:e2 brd ff:ff:ff:ff:ff:ff\n    inet 192.168.2.226/27 scope global eth0\n    ...\n```\nLet's see route table and traceroute:\n```bash\n/ $ ip route\ndefault via 192.168.2.225 dev eth0\n192.168.2.224/27 dev eth0 scope link  src 192.168.2.226\n/ $ traceroute google.com\ntraceroute to google.com (172.217.169.14), 30 hops max, 46 byte packets\n 1  192.168.2.225 (192.168.2.225)  0.019 ms  0.008 ms  0.005 ms\n 2  192.168.2.1 (192.168.2.1)  0.289 ms  0.350 ms  0.332 ms\n 3  126-120-87-11 (126.120.87.11)  0.776 ms  0.660 ms  0.556 ms\n...\n```\nSo we can see that packet from container goes to host interface `192.168.2.225`, then to network\ngateway that is defined on the host machine `192.168.2.1` and continues its way to the destination.\nWe also can connect to containers IP `192.168.2.226` from any server in the `192.168.2.0/24` network.\nIf you can't, try to add a route:\n```bash\nsudo ip route add 192.168.2.226 via 192.168.2.225\n```\nor for whole subnet:\n```bash\nsudo ip route add 192.168.2.224/27 via 192.168.2.225\n```\nMake sure your firewall on the host allows connection. Possibly you'll need to set your interface on the host to\npromiscuous mode:\n```bash\nip link set eth0 promisc on\n```\nThe most important to know here is that now your containers VM is opened to external network, please\ntake into account possible security issues. This is recommended only for test environments\nwith internal non-routable IP ranges.\n\n\n### Bridges and macvlan networks\nAnother option will be defining [macvlan networks](https://docs.docker.com/network/macvlan/#create-a-macvlan-network).\n```bash\ndocker network create -d macvlan --subnet=192.168.2.0/24  --gateway=192.168.2.1 --ip-range=192.168.2.224/27 -o parent=eth0 macvlan_external\n```\nAlthough we defined an existing subnet, we limited ip range to narrow subnet `192.168.2.224/27`. Now we can start virtainers\nas usual:\n```bash\ndocker run -d -t --privileged --name netcontainer --network macvlan_external docker.io/virtainers/centos:7\n```\nIt's possible that you still need to enable promiscuous mode in the interface. Also it's not possible to connect to container\nfrom the same host. But you can connect from other hosts on the network or for example assign an additional IP on the interface\nand connect from it.\n\n## Use cases\n1. Installation of virtual machine with virtainer won't require anything to be installed on the host, except docker or\npodman, it significantly simplifies workflow and make them lighter and shorter.\n2. Continuous integration could be easier when using quick to setup and remove virtual machines, also it can use current\ncontainers plugin or orchestration tools. Virtainers could be managed by any usual for you container management tool.\n3. Orchestration of virtual machines can use now advanced practices from containers world, like Kubernetes for example.\n\nTell us about another ideas and use cases it can be helpful for you.\n\n## What's next?\nWhat is the roadmap:\n1. Create Linux containers with X window, running VNC on startup.\n2. Create a nice python script that will manage all command options.\n3. Create an API.\n4. Create by default networks of docker or podman for virtainers.\n...\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsshnaidm%2Fvirtainers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsshnaidm%2Fvirtainers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsshnaidm%2Fvirtainers/lists"}