Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Running stateful linux containers in docker

⚠️ This is a complete rewrite, using LXC only without vagrant. It is able to run LXC containers created with v0.1 but config and usage differs. Use tag v0.1 to get the old version.

I'm leaving docker hub. New images can be found at ghcr.io/micw/docker-lxc

Old Docker Hub: micwy/lxc

I'm very impressed, how much pulls this image gets. Please let me know how you use this (just create an issue at github), I'll add this to the "Use-Cases" section.

Why?

In some cases, it might be usefull to run full-blown operating systems in a docker environment which have "state", primarily meaning to have a persitent root volume. With docker only, this is not possible since docker does not allow / to be a volume. This is where LXC comes into play. LXC provides a process isolation similar to docker but with statefull root filesystems. Unfortunately, with the rise of docker, management tools for docker are much more widespread and sophisticated than those for LXC.

This project allows to use a single LXC container within a docker container to get best of both worlds.

Features

  • Runs a single LXC container in docker with full OS and persistent root
  • Use features unique to docker for your lxc containers (e.g. docker-compose, exposed ports, traefik for ingress, kubernetes as platform)
  • The LXC container uses the same limits and network stack as the docker container, so things like exposed ports works as expected
  • Proper signal handling in both directions (shutting down the docker container properly shuts down the LXC container. Poweroff in LXC shuts down the docker container)
  • LXCFS support: Within the container, uptime and limits are displayed correctly
  • Shell-Wrapper: If /bin/sh is invoked with "docker exec", a shell in the LXC container is spawned. So a console in most management tools opens directly within the LXC container, not in the surrounding docker container
  • Creation of initial root filesystems: for some distributions, an initial root filesystem can simply be set up, using an environment variable
  • Adding of initial SSH key via environment variable to get instant log-in

Some Use-Cases

  • Provide "home containers" for your users, each with own ssh access and persistent state
  • Run a linux remote desktop server on kubernetes
  • Easily run statefull software (like froxlor control panel or plesk) on docker/kubernetes

Ideas / Backlog

  • Support more distribution root filesystems
  • Import rootfs from vagrant-lxc boxes

How to run

docker run -d \
--name lxc \
--privileged \
--hostname lxctest1 \
-v /path/to/data:/data \
-v /path/to/somedir:/vol/somedir \
-e DISTRIBUTION=alpine \
-e INITIAL_SSH_KEY="ssh-rsa AAAA...Q== my-initial-ssh-key" \
micwy/lxc
  • "privileged" is currently required to run LXC on the container
  • The hostname is passed into the lxc container
  • The volume /data contains the root filesystem (under /data/rootfs) and some additional files (temporary root fs during system creation, lxc config)

Running on Kubernetes

Here's an example yaml to run this on kubernetes. If there's some interest, I can also provide a helm chart.

---
# Source: lxc/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mylxcbox
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
replicas: selector:
matchLabels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
template:
metadata:
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
containers:
- name: lxc
image: "micwy/lxc:latest"
imagePullPolicy: Always
# Required to launch lxc containers in the docker container
securityContext:
privileged: true
# Required to make LXC console work
stdin: true
tty: true
ports:
- name: ssh
containerPort: 22
protocol: TCP
hostPort: 2201
env:
- name: "DISTRIBUTION"
value: "archlinux"
- name: "INITIAL_SSH_KEY"
value: "ssh-rsa ...DVs= my-ssh-key"
volumeMounts:
- mountPath: /data
name: data
# Will be passed into the lxc container
hostname: mylxcbox
volumes:
- name: data
hostPath:
path: /data/mylxcbox
# Strategy must be recreate if hostPort is used
strategy:
type: Recreate

Environment variables

  • DISTRIBUTION: triggers a distribution specific setup script if /data/rootfs does not exist (see below)
  • INITIAL_SSH_KEY: if set, it is copied to /root/.ssh/authorized keys on startup if that file does not exist yet
  • USE_LXCFS (default false): if true, mount LXCFS into the LXC container
    • ⚠️ May not work with systemd!
  • COPY_RESOLV_CONF (default true): if true, copy resolv.conf from docker container into the LXC container

Additional volumes

  • the directory /vol of the docker container is mounted with "rbind" into /vol on the LXC container
  • Every docker-volume that is mounted to /vol/something will appear as /vol/something on LXC

Available distribution setup scripts

DISTRIBUTION: alpine

Installs alpine if rootfs does not exist.

Features:

  • Quite minimal image with bash, nano and openssh

Supported environment variables:

  • ALPINE_ARCH: (default x86_64): architecture of the rootfs
  • ALPINE_VERSION: (default latest-stable): alpine version to install
  • ALPINE_EXTRA_PACKAGES: additional packages to install along with the rootfs

DISTRIBUTION: archlinux

Installs archlinux if rootfs does not exist.

Features:

  • Basic system image with common tools and openssh

Supported environment variables:

  • ARCHLINUX_INSTALL_TRIZEN: (default: true): if true, install the trizen package manager for AUR packages
  • ARCHLINUX_EXTRA_PACKAGES: additional packages to install along with the rootfs. Installation will be run with trizen if installed, otherwise with pacman
  • ARCHLINUX_MIRRORLIST_COUNTRY (default: Germany - I confess, I'm biased): Country to use for create an initial packman mirror list

About

Run stateful lxc containers managed by docker

Resources

Stars

64 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Running stateful linux containers in docker

⚠️ This is a complete rewrite, using LXC only without vagrant. It is able to run LXC containers created with v0.1 but config and usage differs. Use tag v0.1 to get the old version.

I'm leaving docker hub. New images can be found at ghcr.io/micw/docker-lxc

Old Docker Hub: micwy/lxc

I'm very impressed, how much pulls this image gets. Please let me know how you use this (just create an issue at github), I'll add this to the "Use-Cases" section.

Why?

In some cases, it might be usefull to run full-blown operating systems in a docker environment which have "state", primarily meaning to have a persitent root volume. With docker only, this is not possible since docker does not allow / to be a volume. This is where LXC comes into play. LXC provides a process isolation similar to docker but with statefull root filesystems. Unfortunately, with the rise of docker, management tools for docker are much more widespread and sophisticated than those for LXC.

This project allows to use a single LXC container within a docker container to get best of both worlds.

Features

  • Runs a single LXC container in docker with full OS and persistent root
  • Use features unique to docker for your lxc containers (e.g. docker-compose, exposed ports, traefik for ingress, kubernetes as platform)
  • The LXC container uses the same limits and network stack as the docker container, so things like exposed ports works as expected
  • Proper signal handling in both directions (shutting down the docker container properly shuts down the LXC container. Poweroff in LXC shuts down the docker container)
  • LXCFS support: Within the container, uptime and limits are displayed correctly
  • Shell-Wrapper: If /bin/sh is invoked with "docker exec", a shell in the LXC container is spawned. So a console in most management tools opens directly within the LXC container, not in the surrounding docker container
  • Creation of initial root filesystems: for some distributions, an initial root filesystem can simply be set up, using an environment variable
  • Adding of initial SSH key via environment variable to get instant log-in

Some Use-Cases

  • Provide "home containers" for your users, each with own ssh access and persistent state
  • Run a linux remote desktop server on kubernetes
  • Easily run statefull software (like froxlor control panel or plesk) on docker/kubernetes

Ideas / Backlog

  • Support more distribution root filesystems
  • Import rootfs from vagrant-lxc boxes

How to run

docker run -d \
--name lxc \
--privileged \
--hostname lxctest1 \
-v /path/to/data:/data \
-v /path/to/somedir:/vol/somedir \
-e DISTRIBUTION=alpine \
-e INITIAL_SSH_KEY="ssh-rsa AAAA...Q== my-initial-ssh-key" \
micwy/lxc
  • "privileged" is currently required to run LXC on the container
  • The hostname is passed into the lxc container
  • The volume /data contains the root filesystem (under /data/rootfs) and some additional files (temporary root fs during system creation, lxc config)

Running on Kubernetes

Here's an example yaml to run this on kubernetes. If there's some interest, I can also provide a helm chart.

---
# Source: lxc/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mylxcbox
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
replicas: selector:
matchLabels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
template:
metadata:
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
containers:
- name: lxc
image: "micwy/lxc:latest"
imagePullPolicy: Always
# Required to launch lxc containers in the docker container
securityContext:
privileged: true
# Required to make LXC console work
stdin: true
tty: true
ports:
- name: ssh
containerPort: 22
protocol: TCP
hostPort: 2201
env:
- name: "DISTRIBUTION"
value: "archlinux"
- name: "INITIAL_SSH_KEY"
value: "ssh-rsa ...DVs= my-ssh-key"
volumeMounts:
- mountPath: /data
name: data
# Will be passed into the lxc container
hostname: mylxcbox
volumes:
- name: data
hostPath:
path: /data/mylxcbox
# Strategy must be recreate if hostPort is used
strategy:
type: Recreate

Environment variables

  • DISTRIBUTION: triggers a distribution specific setup script if /data/rootfs does not exist (see below)
  • INITIAL_SSH_KEY: if set, it is copied to /root/.ssh/authorized keys on startup if that file does not exist yet
  • USE_LXCFS (default false): if true, mount LXCFS into the LXC container
    • ⚠️ May not work with systemd!
  • COPY_RESOLV_CONF (default true): if true, copy resolv.conf from docker container into the LXC container

Additional volumes

  • the directory /vol of the docker container is mounted with "rbind" into /vol on the LXC container
  • Every docker-volume that is mounted to /vol/something will appear as /vol/something on LXC

Available distribution setup scripts

DISTRIBUTION: alpine

Installs alpine if rootfs does not exist.

Features:

  • Quite minimal image with bash, nano and openssh

Supported environment variables:

  • ALPINE_ARCH: (default x86_64): architecture of the rootfs
  • ALPINE_VERSION: (default latest-stable): alpine version to install
  • ALPINE_EXTRA_PACKAGES: additional packages to install along with the rootfs

DISTRIBUTION: archlinux

Installs archlinux if rootfs does not exist.

Features:

  • Basic system image with common tools and openssh

Supported environment variables:

  • ARCHLINUX_INSTALL_TRIZEN: (default: true): if true, install the trizen package manager for AUR packages
  • ARCHLINUX_EXTRA_PACKAGES: additional packages to install along with the rootfs. Installation will be run with trizen if installed, otherwise with pacman
  • ARCHLINUX_MIRRORLIST_COUNTRY (default: Germany - I confess, I'm biased): Country to use for create an initial packman mirror list

About

Run stateful lxc containers managed by docker

Resources

Stars

64 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Running stateful linux containers in docker

⚠️ This is a complete rewrite, using LXC only without vagrant. It is able to run LXC containers created with v0.1 but config and usage differs. Use tag v0.1 to get the old version.

I'm leaving docker hub. New images can be found at ghcr.io/micw/docker-lxc

Old Docker Hub: micwy/lxc

I'm very impressed, how much pulls this image gets. Please let me know how you use this (just create an issue at github), I'll add this to the "Use-Cases" section.

Why?

In some cases, it might be usefull to run full-blown operating systems in a docker environment which have "state", primarily meaning to have a persitent root volume. With docker only, this is not possible since docker does not allow / to be a volume. This is where LXC comes into play. LXC provides a process isolation similar to docker but with statefull root filesystems. Unfortunately, with the rise of docker, management tools for docker are much more widespread and sophisticated than those for LXC.

This project allows to use a single LXC container within a docker container to get best of both worlds.

Features

  • Runs a single LXC container in docker with full OS and persistent root
  • Use features unique to docker for your lxc containers (e.g. docker-compose, exposed ports, traefik for ingress, kubernetes as platform)
  • The LXC container uses the same limits and network stack as the docker container, so things like exposed ports works as expected
  • Proper signal handling in both directions (shutting down the docker container properly shuts down the LXC container. Poweroff in LXC shuts down the docker container)
  • LXCFS support: Within the container, uptime and limits are displayed correctly
  • Shell-Wrapper: If /bin/sh is invoked with "docker exec", a shell in the LXC container is spawned. So a console in most management tools opens directly within the LXC container, not in the surrounding docker container
  • Creation of initial root filesystems: for some distributions, an initial root filesystem can simply be set up, using an environment variable
  • Adding of initial SSH key via environment variable to get instant log-in

Some Use-Cases

  • Provide "home containers" for your users, each with own ssh access and persistent state
  • Run a linux remote desktop server on kubernetes
  • Easily run statefull software (like froxlor control panel or plesk) on docker/kubernetes

Ideas / Backlog

  • Support more distribution root filesystems
  • Import rootfs from vagrant-lxc boxes

How to run

docker run -d \
--name lxc \
--privileged \
--hostname lxctest1 \
-v /path/to/data:/data \
-v /path/to/somedir:/vol/somedir \
-e DISTRIBUTION=alpine \
-e INITIAL_SSH_KEY="ssh-rsa AAAA...Q== my-initial-ssh-key" \
micwy/lxc
  • "privileged" is currently required to run LXC on the container
  • The hostname is passed into the lxc container
  • The volume /data contains the root filesystem (under /data/rootfs) and some additional files (temporary root fs during system creation, lxc config)

Running on Kubernetes

Here's an example yaml to run this on kubernetes. If there's some interest, I can also provide a helm chart.

---
# Source: lxc/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mylxcbox
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
replicas: selector:
matchLabels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
template:
metadata:
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
containers:
- name: lxc
image: "micwy/lxc:latest"
imagePullPolicy: Always
# Required to launch lxc containers in the docker container
securityContext:
privileged: true
# Required to make LXC console work
stdin: true
tty: true
ports:
- name: ssh
containerPort: 22
protocol: TCP
hostPort: 2201
env:
- name: "DISTRIBUTION"
value: "archlinux"
- name: "INITIAL_SSH_KEY"
value: "ssh-rsa ...DVs= my-ssh-key"
volumeMounts:
- mountPath: /data
name: data
# Will be passed into the lxc container
hostname: mylxcbox
volumes:
- name: data
hostPath:
path: /data/mylxcbox
# Strategy must be recreate if hostPort is used
strategy:
type: Recreate

Environment variables

  • DISTRIBUTION: triggers a distribution specific setup script if /data/rootfs does not exist (see below)
  • INITIAL_SSH_KEY: if set, it is copied to /root/.ssh/authorized keys on startup if that file does not exist yet
  • USE_LXCFS (default false): if true, mount LXCFS into the LXC container
    • ⚠️ May not work with systemd!
  • COPY_RESOLV_CONF (default true): if true, copy resolv.conf from docker container into the LXC container

Additional volumes

  • the directory /vol of the docker container is mounted with "rbind" into /vol on the LXC container
  • Every docker-volume that is mounted to /vol/something will appear as /vol/something on LXC

Available distribution setup scripts

DISTRIBUTION: alpine

Installs alpine if rootfs does not exist.

Features:

  • Quite minimal image with bash, nano and openssh

Supported environment variables:

  • ALPINE_ARCH: (default x86_64): architecture of the rootfs
  • ALPINE_VERSION: (default latest-stable): alpine version to install
  • ALPINE_EXTRA_PACKAGES: additional packages to install along with the rootfs

DISTRIBUTION: archlinux

Installs archlinux if rootfs does not exist.

Features:

  • Basic system image with common tools and openssh

Supported environment variables:

  • ARCHLINUX_INSTALL_TRIZEN: (default: true): if true, install the trizen package manager for AUR packages
  • ARCHLINUX_EXTRA_PACKAGES: additional packages to install along with the rootfs. Installation will be run with trizen if installed, otherwise with pacman
  • ARCHLINUX_MIRRORLIST_COUNTRY (default: Germany - I confess, I'm biased): Country to use for create an initial packman mirror list

About

Run stateful lxc containers managed by docker

Resources

Stars

64 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Running stateful linux containers in docker

⚠️ This is a complete rewrite, using LXC only without vagrant. It is able to run LXC containers created with v0.1 but config and usage differs. Use tag v0.1 to get the old version.

I'm leaving docker hub. New images can be found at ghcr.io/micw/docker-lxc

Old Docker Hub: micwy/lxc

I'm very impressed, how much pulls this image gets. Please let me know how you use this (just create an issue at github), I'll add this to the "Use-Cases" section.

Why?

In some cases, it might be usefull to run full-blown operating systems in a docker environment which have "state", primarily meaning to have a persitent root volume. With docker only, this is not possible since docker does not allow / to be a volume. This is where LXC comes into play. LXC provides a process isolation similar to docker but with statefull root filesystems. Unfortunately, with the rise of docker, management tools for docker are much more widespread and sophisticated than those for LXC.

This project allows to use a single LXC container within a docker container to get best of both worlds.

Features

  • Runs a single LXC container in docker with full OS and persistent root
  • Use features unique to docker for your lxc containers (e.g. docker-compose, exposed ports, traefik for ingress, kubernetes as platform)
  • The LXC container uses the same limits and network stack as the docker container, so things like exposed ports works as expected
  • Proper signal handling in both directions (shutting down the docker container properly shuts down the LXC container. Poweroff in LXC shuts down the docker container)
  • LXCFS support: Within the container, uptime and limits are displayed correctly
  • Shell-Wrapper: If /bin/sh is invoked with "docker exec", a shell in the LXC container is spawned. So a console in most management tools opens directly within the LXC container, not in the surrounding docker container
  • Creation of initial root filesystems: for some distributions, an initial root filesystem can simply be set up, using an environment variable
  • Adding of initial SSH key via environment variable to get instant log-in

Some Use-Cases

  • Provide "home containers" for your users, each with own ssh access and persistent state
  • Run a linux remote desktop server on kubernetes
  • Easily run statefull software (like froxlor control panel or plesk) on docker/kubernetes

Ideas / Backlog

  • Support more distribution root filesystems
  • Import rootfs from vagrant-lxc boxes

How to run

docker run -d \
--name lxc \
--privileged \
--hostname lxctest1 \
-v /path/to/data:/data \
-v /path/to/somedir:/vol/somedir \
-e DISTRIBUTION=alpine \
-e INITIAL_SSH_KEY="ssh-rsa AAAA...Q== my-initial-ssh-key" \
micwy/lxc
  • "privileged" is currently required to run LXC on the container
  • The hostname is passed into the lxc container
  • The volume /data contains the root filesystem (under /data/rootfs) and some additional files (temporary root fs during system creation, lxc config)

Running on Kubernetes

Here's an example yaml to run this on kubernetes. If there's some interest, I can also provide a helm chart.

---
# Source: lxc/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mylxcbox
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
replicas: selector:
matchLabels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
template:
metadata:
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
containers:
- name: lxc
image: "micwy/lxc:latest"
imagePullPolicy: Always
# Required to launch lxc containers in the docker container
securityContext:
privileged: true
# Required to make LXC console work
stdin: true
tty: true
ports:
- name: ssh
containerPort: 22
protocol: TCP
hostPort: 2201
env:
- name: "DISTRIBUTION"
value: "archlinux"
- name: "INITIAL_SSH_KEY"
value: "ssh-rsa ...DVs= my-ssh-key"
volumeMounts:
- mountPath: /data
name: data
# Will be passed into the lxc container
hostname: mylxcbox
volumes:
- name: data
hostPath:
path: /data/mylxcbox
# Strategy must be recreate if hostPort is used
strategy:
type: Recreate

Environment variables

  • DISTRIBUTION: triggers a distribution specific setup script if /data/rootfs does not exist (see below)
  • INITIAL_SSH_KEY: if set, it is copied to /root/.ssh/authorized keys on startup if that file does not exist yet
  • USE_LXCFS (default false): if true, mount LXCFS into the LXC container
    • ⚠️ May not work with systemd!
  • COPY_RESOLV_CONF (default true): if true, copy resolv.conf from docker container into the LXC container

Additional volumes

  • the directory /vol of the docker container is mounted with "rbind" into /vol on the LXC container
  • Every docker-volume that is mounted to /vol/something will appear as /vol/something on LXC

Available distribution setup scripts

DISTRIBUTION: alpine

Installs alpine if rootfs does not exist.

Features:

  • Quite minimal image with bash, nano and openssh

Supported environment variables:

  • ALPINE_ARCH: (default x86_64): architecture of the rootfs
  • ALPINE_VERSION: (default latest-stable): alpine version to install
  • ALPINE_EXTRA_PACKAGES: additional packages to install along with the rootfs

DISTRIBUTION: archlinux

Installs archlinux if rootfs does not exist.

Features:

  • Basic system image with common tools and openssh

Supported environment variables:

  • ARCHLINUX_INSTALL_TRIZEN: (default: true): if true, install the trizen package manager for AUR packages
  • ARCHLINUX_EXTRA_PACKAGES: additional packages to install along with the rootfs. Installation will be run with trizen if installed, otherwise with pacman
  • ARCHLINUX_MIRRORLIST_COUNTRY (default: Germany - I confess, I'm biased): Country to use for create an initial packman mirror list

About

Run stateful lxc containers managed by docker

Resources

Stars

64 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Running stateful linux containers in docker

⚠️ This is a complete rewrite, using LXC only without vagrant. It is able to run LXC containers created with v0.1 but config and usage differs. Use tag v0.1 to get the old version.

I'm leaving docker hub. New images can be found at ghcr.io/micw/docker-lxc

Old Docker Hub: micwy/lxc

I'm very impressed, how much pulls this image gets. Please let me know how you use this (just create an issue at github), I'll add this to the "Use-Cases" section.

Why?

In some cases, it might be usefull to run full-blown operating systems in a docker environment which have "state", primarily meaning to have a persitent root volume. With docker only, this is not possible since docker does not allow / to be a volume. This is where LXC comes into play. LXC provides a process isolation similar to docker but with statefull root filesystems. Unfortunately, with the rise of docker, management tools for docker are much more widespread and sophisticated than those for LXC.

This project allows to use a single LXC container within a docker container to get best of both worlds.

Features

  • Runs a single LXC container in docker with full OS and persistent root
  • Use features unique to docker for your lxc containers (e.g. docker-compose, exposed ports, traefik for ingress, kubernetes as platform)
  • The LXC container uses the same limits and network stack as the docker container, so things like exposed ports works as expected
  • Proper signal handling in both directions (shutting down the docker container properly shuts down the LXC container. Poweroff in LXC shuts down the docker container)
  • LXCFS support: Within the container, uptime and limits are displayed correctly
  • Shell-Wrapper: If /bin/sh is invoked with "docker exec", a shell in the LXC container is spawned. So a console in most management tools opens directly within the LXC container, not in the surrounding docker container
  • Creation of initial root filesystems: for some distributions, an initial root filesystem can simply be set up, using an environment variable
  • Adding of initial SSH key via environment variable to get instant log-in

Some Use-Cases

  • Provide "home containers" for your users, each with own ssh access and persistent state
  • Run a linux remote desktop server on kubernetes
  • Easily run statefull software (like froxlor control panel or plesk) on docker/kubernetes

Ideas / Backlog

  • Support more distribution root filesystems
  • Import rootfs from vagrant-lxc boxes

How to run

docker run -d \
--name lxc \
--privileged \
--hostname lxctest1 \
-v /path/to/data:/data \
-v /path/to/somedir:/vol/somedir \
-e DISTRIBUTION=alpine \
-e INITIAL_SSH_KEY="ssh-rsa AAAA...Q== my-initial-ssh-key" \
micwy/lxc
  • "privileged" is currently required to run LXC on the container
  • The hostname is passed into the lxc container
  • The volume /data contains the root filesystem (under /data/rootfs) and some additional files (temporary root fs during system creation, lxc config)

Running on Kubernetes

Here's an example yaml to run this on kubernetes. If there's some interest, I can also provide a helm chart.

---
# Source: lxc/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mylxcbox
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
replicas: selector:
matchLabels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
template:
metadata:
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
containers:
- name: lxc
image: "micwy/lxc:latest"
imagePullPolicy: Always
# Required to launch lxc containers in the docker container
securityContext:
privileged: true
# Required to make LXC console work
stdin: true
tty: true
ports:
- name: ssh
containerPort: 22
protocol: TCP
hostPort: 2201
env:
- name: "DISTRIBUTION"
value: "archlinux"
- name: "INITIAL_SSH_KEY"
value: "ssh-rsa ...DVs= my-ssh-key"
volumeMounts:
- mountPath: /data
name: data
# Will be passed into the lxc container
hostname: mylxcbox
volumes:
- name: data
hostPath:
path: /data/mylxcbox
# Strategy must be recreate if hostPort is used
strategy:
type: Recreate

Environment variables

  • DISTRIBUTION: triggers a distribution specific setup script if /data/rootfs does not exist (see below)
  • INITIAL_SSH_KEY: if set, it is copied to /root/.ssh/authorized keys on startup if that file does not exist yet
  • USE_LXCFS (default false): if true, mount LXCFS into the LXC container
    • ⚠️ May not work with systemd!
  • COPY_RESOLV_CONF (default true): if true, copy resolv.conf from docker container into the LXC container

Additional volumes

  • the directory /vol of the docker container is mounted with "rbind" into /vol on the LXC container
  • Every docker-volume that is mounted to /vol/something will appear as /vol/something on LXC

Available distribution setup scripts

DISTRIBUTION: alpine

Installs alpine if rootfs does not exist.

Features:

  • Quite minimal image with bash, nano and openssh

Supported environment variables:

  • ALPINE_ARCH: (default x86_64): architecture of the rootfs
  • ALPINE_VERSION: (default latest-stable): alpine version to install
  • ALPINE_EXTRA_PACKAGES: additional packages to install along with the rootfs

DISTRIBUTION: archlinux

Installs archlinux if rootfs does not exist.

Features:

  • Basic system image with common tools and openssh

Supported environment variables:

  • ARCHLINUX_INSTALL_TRIZEN: (default: true): if true, install the trizen package manager for AUR packages
  • ARCHLINUX_EXTRA_PACKAGES: additional packages to install along with the rootfs. Installation will be run with trizen if installed, otherwise with pacman
  • ARCHLINUX_MIRRORLIST_COUNTRY (default: Germany - I confess, I'm biased): Country to use for create an initial packman mirror list

About

Run stateful lxc containers managed by docker

Resources

Stars

64 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Running stateful linux containers in docker

⚠️ This is a complete rewrite, using LXC only without vagrant. It is able to run LXC containers created with v0.1 but config and usage differs. Use tag v0.1 to get the old version.

I'm leaving docker hub. New images can be found at ghcr.io/micw/docker-lxc

Old Docker Hub: micwy/lxc

I'm very impressed, how much pulls this image gets. Please let me know how you use this (just create an issue at github), I'll add this to the "Use-Cases" section.

Why?

In some cases, it might be usefull to run full-blown operating systems in a docker environment which have "state", primarily meaning to have a persitent root volume. With docker only, this is not possible since docker does not allow / to be a volume. This is where LXC comes into play. LXC provides a process isolation similar to docker but with statefull root filesystems. Unfortunately, with the rise of docker, management tools for docker are much more widespread and sophisticated than those for LXC.

This project allows to use a single LXC container within a docker container to get best of both worlds.

Features

  • Runs a single LXC container in docker with full OS and persistent root
  • Use features unique to docker for your lxc containers (e.g. docker-compose, exposed ports, traefik for ingress, kubernetes as platform)
  • The LXC container uses the same limits and network stack as the docker container, so things like exposed ports works as expected
  • Proper signal handling in both directions (shutting down the docker container properly shuts down the LXC container. Poweroff in LXC shuts down the docker container)
  • LXCFS support: Within the container, uptime and limits are displayed correctly
  • Shell-Wrapper: If /bin/sh is invoked with "docker exec", a shell in the LXC container is spawned. So a console in most management tools opens directly within the LXC container, not in the surrounding docker container
  • Creation of initial root filesystems: for some distributions, an initial root filesystem can simply be set up, using an environment variable
  • Adding of initial SSH key via environment variable to get instant log-in

Some Use-Cases

  • Provide "home containers" for your users, each with own ssh access and persistent state
  • Run a linux remote desktop server on kubernetes
  • Easily run statefull software (like froxlor control panel or plesk) on docker/kubernetes

Ideas / Backlog

  • Support more distribution root filesystems
  • Import rootfs from vagrant-lxc boxes

How to run

docker run -d \
--name lxc \
--privileged \
--hostname lxctest1 \
-v /path/to/data:/data \
-v /path/to/somedir:/vol/somedir \
-e DISTRIBUTION=alpine \
-e INITIAL_SSH_KEY="ssh-rsa AAAA...Q== my-initial-ssh-key" \
micwy/lxc
  • "privileged" is currently required to run LXC on the container
  • The hostname is passed into the lxc container
  • The volume /data contains the root filesystem (under /data/rootfs) and some additional files (temporary root fs during system creation, lxc config)

Running on Kubernetes

Here's an example yaml to run this on kubernetes. If there's some interest, I can also provide a helm chart.

---
# Source: lxc/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mylxcbox
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
replicas: selector:
matchLabels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
template:
metadata:
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
containers:
- name: lxc
image: "micwy/lxc:latest"
imagePullPolicy: Always
# Required to launch lxc containers in the docker container
securityContext:
privileged: true
# Required to make LXC console work
stdin: true
tty: true
ports:
- name: ssh
containerPort: 22
protocol: TCP
hostPort: 2201
env:
- name: "DISTRIBUTION"
value: "archlinux"
- name: "INITIAL_SSH_KEY"
value: "ssh-rsa ...DVs= my-ssh-key"
volumeMounts:
- mountPath: /data
name: data
# Will be passed into the lxc container
hostname: mylxcbox
volumes:
- name: data
hostPath:
path: /data/mylxcbox
# Strategy must be recreate if hostPort is used
strategy:
type: Recreate

Environment variables

  • DISTRIBUTION: triggers a distribution specific setup script if /data/rootfs does not exist (see below)
  • INITIAL_SSH_KEY: if set, it is copied to /root/.ssh/authorized keys on startup if that file does not exist yet
  • USE_LXCFS (default false): if true, mount LXCFS into the LXC container
    • ⚠️ May not work with systemd!
  • COPY_RESOLV_CONF (default true): if true, copy resolv.conf from docker container into the LXC container

Additional volumes

  • the directory /vol of the docker container is mounted with "rbind" into /vol on the LXC container
  • Every docker-volume that is mounted to /vol/something will appear as /vol/something on LXC

Available distribution setup scripts

DISTRIBUTION: alpine

Installs alpine if rootfs does not exist.

Features:

  • Quite minimal image with bash, nano and openssh

Supported environment variables:

  • ALPINE_ARCH: (default x86_64): architecture of the rootfs
  • ALPINE_VERSION: (default latest-stable): alpine version to install
  • ALPINE_EXTRA_PACKAGES: additional packages to install along with the rootfs

DISTRIBUTION: archlinux

Installs archlinux if rootfs does not exist.

Features:

  • Basic system image with common tools and openssh

Supported environment variables:

  • ARCHLINUX_INSTALL_TRIZEN: (default: true): if true, install the trizen package manager for AUR packages
  • ARCHLINUX_EXTRA_PACKAGES: additional packages to install along with the rootfs. Installation will be run with trizen if installed, otherwise with pacman
  • ARCHLINUX_MIRRORLIST_COUNTRY (default: Germany - I confess, I'm biased): Country to use for create an initial packman mirror list

About

Run stateful lxc containers managed by docker

Resources

Stars

64 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Running stateful linux containers in docker

⚠️ This is a complete rewrite, using LXC only without vagrant. It is able to run LXC containers created with v0.1 but config and usage differs. Use tag v0.1 to get the old version.

I'm leaving docker hub. New images can be found at ghcr.io/micw/docker-lxc

Old Docker Hub: micwy/lxc

I'm very impressed, how much pulls this image gets. Please let me know how you use this (just create an issue at github), I'll add this to the "Use-Cases" section.

Why?

In some cases, it might be usefull to run full-blown operating systems in a docker environment which have "state", primarily meaning to have a persitent root volume. With docker only, this is not possible since docker does not allow / to be a volume. This is where LXC comes into play. LXC provides a process isolation similar to docker but with statefull root filesystems. Unfortunately, with the rise of docker, management tools for docker are much more widespread and sophisticated than those for LXC.

This project allows to use a single LXC container within a docker container to get best of both worlds.

Features

  • Runs a single LXC container in docker with full OS and persistent root
  • Use features unique to docker for your lxc containers (e.g. docker-compose, exposed ports, traefik for ingress, kubernetes as platform)
  • The LXC container uses the same limits and network stack as the docker container, so things like exposed ports works as expected
  • Proper signal handling in both directions (shutting down the docker container properly shuts down the LXC container. Poweroff in LXC shuts down the docker container)
  • LXCFS support: Within the container, uptime and limits are displayed correctly
  • Shell-Wrapper: If /bin/sh is invoked with "docker exec", a shell in the LXC container is spawned. So a console in most management tools opens directly within the LXC container, not in the surrounding docker container
  • Creation of initial root filesystems: for some distributions, an initial root filesystem can simply be set up, using an environment variable
  • Adding of initial SSH key via environment variable to get instant log-in

Some Use-Cases

  • Provide "home containers" for your users, each with own ssh access and persistent state
  • Run a linux remote desktop server on kubernetes
  • Easily run statefull software (like froxlor control panel or plesk) on docker/kubernetes

Ideas / Backlog

  • Support more distribution root filesystems
  • Import rootfs from vagrant-lxc boxes

How to run

docker run -d \
--name lxc \
--privileged \
--hostname lxctest1 \
-v /path/to/data:/data \
-v /path/to/somedir:/vol/somedir \
-e DISTRIBUTION=alpine \
-e INITIAL_SSH_KEY="ssh-rsa AAAA...Q== my-initial-ssh-key" \
micwy/lxc
  • "privileged" is currently required to run LXC on the container
  • The hostname is passed into the lxc container
  • The volume /data contains the root filesystem (under /data/rootfs) and some additional files (temporary root fs during system creation, lxc config)

Running on Kubernetes

Here's an example yaml to run this on kubernetes. If there's some interest, I can also provide a helm chart.

---
# Source: lxc/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mylxcbox
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
replicas: selector:
matchLabels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
template:
metadata:
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
containers:
- name: lxc
image: "micwy/lxc:latest"
imagePullPolicy: Always
# Required to launch lxc containers in the docker container
securityContext:
privileged: true
# Required to make LXC console work
stdin: true
tty: true
ports:
- name: ssh
containerPort: 22
protocol: TCP
hostPort: 2201
env:
- name: "DISTRIBUTION"
value: "archlinux"
- name: "INITIAL_SSH_KEY"
value: "ssh-rsa ...DVs= my-ssh-key"
volumeMounts:
- mountPath: /data
name: data
# Will be passed into the lxc container
hostname: mylxcbox
volumes:
- name: data
hostPath:
path: /data/mylxcbox
# Strategy must be recreate if hostPort is used
strategy:
type: Recreate

Environment variables

  • DISTRIBUTION: triggers a distribution specific setup script if /data/rootfs does not exist (see below)
  • INITIAL_SSH_KEY: if set, it is copied to /root/.ssh/authorized keys on startup if that file does not exist yet
  • USE_LXCFS (default false): if true, mount LXCFS into the LXC container
    • ⚠️ May not work with systemd!
  • COPY_RESOLV_CONF (default true): if true, copy resolv.conf from docker container into the LXC container

Additional volumes

  • the directory /vol of the docker container is mounted with "rbind" into /vol on the LXC container
  • Every docker-volume that is mounted to /vol/something will appear as /vol/something on LXC

Available distribution setup scripts

DISTRIBUTION: alpine

Installs alpine if rootfs does not exist.

Features:

  • Quite minimal image with bash, nano and openssh

Supported environment variables:

  • ALPINE_ARCH: (default x86_64): architecture of the rootfs
  • ALPINE_VERSION: (default latest-stable): alpine version to install
  • ALPINE_EXTRA_PACKAGES: additional packages to install along with the rootfs

DISTRIBUTION: archlinux

Installs archlinux if rootfs does not exist.

Features:

  • Basic system image with common tools and openssh

Supported environment variables:

  • ARCHLINUX_INSTALL_TRIZEN: (default: true): if true, install the trizen package manager for AUR packages
  • ARCHLINUX_EXTRA_PACKAGES: additional packages to install along with the rootfs. Installation will be run with trizen if installed, otherwise with pacman
  • ARCHLINUX_MIRRORLIST_COUNTRY (default: Germany - I confess, I'm biased): Country to use for create an initial packman mirror list

About

Run stateful lxc containers managed by docker

Resources

Stars

64 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Running stateful linux containers in docker

⚠️ This is a complete rewrite, using LXC only without vagrant. It is able to run LXC containers created with v0.1 but config and usage differs. Use tag v0.1 to get the old version.

I'm leaving docker hub. New images can be found at ghcr.io/micw/docker-lxc

Old Docker Hub: micwy/lxc

I'm very impressed, how much pulls this image gets. Please let me know how you use this (just create an issue at github), I'll add this to the "Use-Cases" section.

Why?

In some cases, it might be usefull to run full-blown operating systems in a docker environment which have "state", primarily meaning to have a persitent root volume. With docker only, this is not possible since docker does not allow / to be a volume. This is where LXC comes into play. LXC provides a process isolation similar to docker but with statefull root filesystems. Unfortunately, with the rise of docker, management tools for docker are much more widespread and sophisticated than those for LXC.

This project allows to use a single LXC container within a docker container to get best of both worlds.

Features

  • Runs a single LXC container in docker with full OS and persistent root
  • Use features unique to docker for your lxc containers (e.g. docker-compose, exposed ports, traefik for ingress, kubernetes as platform)
  • The LXC container uses the same limits and network stack as the docker container, so things like exposed ports works as expected
  • Proper signal handling in both directions (shutting down the docker container properly shuts down the LXC container. Poweroff in LXC shuts down the docker container)
  • LXCFS support: Within the container, uptime and limits are displayed correctly
  • Shell-Wrapper: If /bin/sh is invoked with "docker exec", a shell in the LXC container is spawned. So a console in most management tools opens directly within the LXC container, not in the surrounding docker container
  • Creation of initial root filesystems: for some distributions, an initial root filesystem can simply be set up, using an environment variable
  • Adding of initial SSH key via environment variable to get instant log-in

Some Use-Cases

  • Provide "home containers" for your users, each with own ssh access and persistent state
  • Run a linux remote desktop server on kubernetes
  • Easily run statefull software (like froxlor control panel or plesk) on docker/kubernetes

Ideas / Backlog

  • Support more distribution root filesystems
  • Import rootfs from vagrant-lxc boxes

How to run

docker run -d \
--name lxc \
--privileged \
--hostname lxctest1 \
-v /path/to/data:/data \
-v /path/to/somedir:/vol/somedir \
-e DISTRIBUTION=alpine \
-e INITIAL_SSH_KEY="ssh-rsa AAAA...Q== my-initial-ssh-key" \
micwy/lxc
  • "privileged" is currently required to run LXC on the container
  • The hostname is passed into the lxc container
  • The volume /data contains the root filesystem (under /data/rootfs) and some additional files (temporary root fs during system creation, lxc config)

Running on Kubernetes

Here's an example yaml to run this on kubernetes. If there's some interest, I can also provide a helm chart.

---
# Source: lxc/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: mylxcbox
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
replicas: selector:
matchLabels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
template:
metadata:
labels:
app.kubernetes.io/name: lxc
app.kubernetes.io/instance: mylxcbox
spec:
containers:
- name: lxc
image: "micwy/lxc:latest"
imagePullPolicy: Always
# Required to launch lxc containers in the docker container
securityContext:
privileged: true
# Required to make LXC console work
stdin: true
tty: true
ports:
- name: ssh
containerPort: 22
protocol: TCP
hostPort: 2201
env:
- name: "DISTRIBUTION"
value: "archlinux"
- name: "INITIAL_SSH_KEY"
value: "ssh-rsa ...DVs= my-ssh-key"
volumeMounts:
- mountPath: /data
name: data
# Will be passed into the lxc container
hostname: mylxcbox
volumes:
- name: data
hostPath:
path: /data/mylxcbox
# Strategy must be recreate if hostPort is used
strategy:
type: Recreate

Environment variables

  • DISTRIBUTION: triggers a distribution specific setup script if /data/rootfs does not exist (see below)
  • INITIAL_SSH_KEY: if set, it is copied to /root/.ssh/authorized keys on startup if that file does not exist yet
  • USE_LXCFS (default false): if true, mount LXCFS into the LXC container
    • ⚠️ May not work with systemd!
  • COPY_RESOLV_CONF (default true): if true, copy resolv.conf from docker container into the LXC container

Additional volumes

  • the directory /vol of the docker container is mounted with "rbind" into /vol on the LXC container
  • Every docker-volume that is mounted to /vol/something will appear as /vol/something on LXC

Available distribution setup scripts

DISTRIBUTION: alpine

Installs alpine if rootfs does not exist.

Features:

  • Quite minimal image with bash, nano and openssh

Supported environment variables:

  • ALPINE_ARCH: (default x86_64): architecture of the rootfs
  • ALPINE_VERSION: (default latest-stable): alpine version to install
  • ALPINE_EXTRA_PACKAGES: additional packages to install along with the rootfs

DISTRIBUTION: archlinux

Installs archlinux if rootfs does not exist.

Features:

  • Basic system image with common tools and openssh

Supported environment variables:

  • ARCHLINUX_INSTALL_TRIZEN: (default: true): if true, install the trizen package manager for AUR packages
  • ARCHLINUX_EXTRA_PACKAGES: additional packages to install along with the rootfs. Installation will be run with trizen if installed, otherwise with pacman
  • ARCHLINUX_MIRRORLIST_COUNTRY (default: Germany - I confess, I'm biased): Country to use for create an initial packman mirror list

About

Run stateful lxc containers managed by docker

Resources

Stars

64 stars

Watchers

3 watching

Forks

Releases

Packages

Used by

Contributors

Languages