Overview
SmolVM provides a robust system for managing environment variables inside running microVMs. Variables are persisted to /etc/profile.d/smolvm_env.sh and automatically loaded in new login shells, making them available across SSH sessions.
Quick Start
from smolvm import SmolVM
with SmolVM() as vm:
# Set environment variables
vm.set_env_vars({ "API_KEY" : "secret-123" , "DEBUG" : "true" })
# Use them in commands
result = vm.run( "echo $API_KEY" )
print (result.output) # secret-123
# List current variables
env_vars = vm.list_env_vars()
print (env_vars) # {'API_KEY': 'secret-123', 'DEBUG': 'true'}
# Remove variables
removed = vm.unset_env_vars([ "DEBUG" ])
print (removed) # {'DEBUG': 'true'}
Setting Environment Variables
Basic Usage
with SmolVM() as vm:
# Set multiple variables
vm.set_env_vars({
"APP_ENV" : "production" ,
"LOG_LEVEL" : "info" ,
"MAX_WORKERS" : "4"
})
Merge vs Replace
By default, set_env_vars() merges with existing variables:
# First set
vm.set_env_vars({ "FOO" : "bar" , "BAZ" : "qux" })
# Second set (merge=True by default)
vm.set_env_vars({ "FOO" : "updated" , "NEW" : "value" })
# Result: {'FOO': 'updated', 'BAZ': 'qux', 'NEW': 'value'}
print (vm.list_env_vars())
To replace all variables:
# Replace entire environment
vm.set_env_vars({ "ONLY" : "this" }, merge = False )
# Result: {'ONLY': 'this'}
print (vm.list_env_vars())
The set_env_vars() method returns a sorted list of all variable names present after the update.
Return Value
keys = vm.set_env_vars({ "KEY1" : "val1" , "KEY2" : "val2" })
print (keys) # ['KEY1', 'KEY2']
Boot-Time Injection
Inject environment variables when creating a VM:
from smolvm import SmolVM, VMConfig
from smolvm.build import SSH_BOOT_ARGS
config = VMConfig(
vm_id = "api-server" ,
vcpu_count = 1 ,
mem_size_mib = 512 ,
kernel_path = kernel,
rootfs_path = rootfs,
boot_args = SSH_BOOT_ARGS ,
env_vars = {
"DATABASE_URL" : "postgres://localhost/mydb" ,
"API_TOKEN" : "secret-token-123" ,
"ENVIRONMENT" : "staging"
}
)
with SmolVM(config) as vm:
# Variables already available
result = vm.run( "echo $DATABASE_URL" )
print (result.output) # postgres://localhost/mydb
Boot-time injection requires an SSH-capable image. The VM must be booted with init=/init in boot args, or injection will fail.
Listing Environment Variables
env_vars = vm.list_env_vars()
for key, value in env_vars.items():
print ( f " { key } = { value } " )
The list_env_vars() method:
Returns a dictionary of all SmolVM-managed variables
Only includes variables in /etc/profile.d/smolvm_env.sh
Does not include system environment variables
Returns empty dict if no variables are set
Removing Environment Variables
Single Variable
removed = vm.unset_env_vars([ "OLD_KEY" ])
if "OLD_KEY" in removed:
print ( f "Removed OLD_KEY with value: { removed[ 'OLD_KEY' ] } " )
Multiple Variables
removed = vm.unset_env_vars([ "KEY1" , "KEY2" , "KEY3" ])
print ( f "Removed { len (removed) } variables" )
Return Value
# Set some variables
vm.set_env_vars({ "A" : "1" , "B" : "2" , "C" : "3" })
# Remove some (including a non-existent one)
removed = vm.unset_env_vars([ "A" , "C" , "NONEXISTENT" ])
# Returns only variables that actually existed
print (removed) # {'A': '1', 'C': '3'}
# Remaining variables
print (vm.list_env_vars()) # {'B': '2'}
Variable Naming Rules
Environment variable keys must be valid shell identifiers:
# Valid keys
vm.set_env_vars({
"API_KEY" : "value" ,
"db_host" : "localhost" ,
"_private" : "secret" ,
"VAR123" : "test"
})
# Invalid keys (will raise ValueError)
try :
vm.set_env_vars({ "123invalid" : "value" }) # Can't start with number
except ValueError as e:
print (e) # Invalid environment variable key
try :
vm.set_env_vars({ "has-dash" : "value" }) # Dashes not allowed
except ValueError as e:
print (e)
Keys must match the regex: ^[A-Za-z_][A-Za-z0-9_]*$
Start with letter or underscore
Contain only letters, numbers, and underscores
No spaces, dashes, or special characters
Value Handling
Special Characters
SmolVM automatically handles special characters in values:
vm.set_env_vars({
"PASSWORD" : "p@ssw0rd!#$%" ,
"PATH_WITH_SPACES" : "/path/with spaces/file.txt" ,
"JSON_CONFIG" : '{"key": "value", "nested": {"a": 1 }} ' ,
"QUOTES" : "Value with 'single' and \" double \" quotes"
})
# All values are safely escaped and preserved
result = vm.run( "echo $PASSWORD" )
print (result.output) # p@ssw0rd!#$%
Multiline Values
multiline_script = """
#!/bin/bash
echo "Line 1"
echo "Line 2"
"""
vm.set_env_vars({ "SCRIPT" : multiline_script})
Persistence and Scope
File Location
Variables are stored in /etc/profile.d/smolvm_env.sh inside the guest:
with SmolVM() as vm:
vm.set_env_vars({ "TEST" : "value" })
# View the generated file
result = vm.run( "cat /etc/profile.d/smolvm_env.sh" )
print (result.output)
# Output:
# #!/bin/sh
# # SmolVM managed environment variables
#
# export TEST='value'
When Variables Are Available
Variables are loaded by:
Login shells (shell="login" mode in vm.run())
SSH sessions (new connections)
Interactive shell sessions
Variables are not loaded by:
Raw shell mode (shell="raw")
Non-login shells
Processes started before the variables were set
Login Shell (Loaded)
Raw Mode (Not Loaded)
vm.set_env_vars({ "MY_VAR" : "hello" })
# Login shell loads the variable
result = vm.run( "echo $MY_VAR" , shell = "login" )
print (result.output) # hello
Real-World Examples
API Credentials
import os
from smolvm import SmolVM
# Get credentials from host environment
host_api_key = os.getenv( "OPENAI_API_KEY" )
with SmolVM() as vm:
# Inject into guest
if host_api_key:
vm.set_env_vars({ "OPENAI_API_KEY" : host_api_key})
# Install and run a tool that needs the API key
vm.run( "apk add --no-cache python3 py3-pip" )
vm.run( "pip3 install openai" )
result = vm.run( """
python3 -c '
import os
api_key = os.environ.get("OPENAI_API_KEY")
print(f"API key configured: {bool(api_key)}")
'
""" )
print (result.output)
Application Configuration
from smolvm import SmolVM, VMConfig
from smolvm.build import SSH_BOOT_ARGS
config = VMConfig(
vm_id = "web-app" ,
vcpu_count = 2 ,
mem_size_mib = 1024 ,
kernel_path = kernel,
rootfs_path = rootfs,
boot_args = SSH_BOOT_ARGS ,
env_vars = {
"APP_ENV" : "production" ,
"LOG_LEVEL" : "info" ,
"DATABASE_URL" : "postgres://db.example.com/prod" ,
"REDIS_URL" : "redis://cache.example.com:6379" ,
"MAX_CONNECTIONS" : "100" ,
"ENABLE_FEATURE_X" : "true"
}
)
with SmolVM(config) as vm:
# Deploy and run application
vm.run( "apk add --no-cache python3 py3-pip" )
vm.run( "pip3 install flask psycopg2 redis" )
# App reads config from environment
Dynamic Configuration Updates
from smolvm import SmolVM
with SmolVM() as vm:
# Initial configuration
vm.set_env_vars({
"LOG_LEVEL" : "info" ,
"CACHE_TTL" : "3600"
})
# Run application
vm.run( "./start-app.sh &" )
# Later: update configuration without restart
vm.set_env_vars({ "LOG_LEVEL" : "debug" })
# New sessions get updated config
result = vm.run( "echo $LOG_LEVEL" )
print (result.output) # debug
Feature Flags
with SmolVM() as vm:
# Enable/disable features via env vars
vm.set_env_vars({
"FEATURE_NEW_UI" : "enabled" ,
"FEATURE_BETA_API" : "disabled" ,
"FEATURE_ANALYTICS" : "enabled"
})
# Application checks these at runtime
result = vm.run( """
python3 -c '
import os
if os.getenv("FEATURE_NEW_UI") == "enabled":
print("Using new UI")
else:
print("Using legacy UI")
'
""" )
print (result.output)
Security Considerations
Secrets Management
Environment variables are stored in plaintext in /etc/profile.d/smolvm_env.sh. For sensitive data, consider:
Using ephemeral VMs that are deleted after use
Encrypting sensitive values before injection
Using secret management services inside the VM
import base64
# Example: Basic obfuscation (not encryption!)
secret = "sensitive-api-key"
obfuscated = base64.b64encode(secret.encode()).decode()
vm.set_env_vars({ "ENCODED_SECRET" : obfuscated})
# Decode in the guest
result = vm.run( "echo $ENCODED_SECRET | base64 -d" )
print (result.output) # sensitive-api-key
Isolation
Each VM has its own isolated environment:
with SmolVM() as vm1:
vm1.set_env_vars({ "INSTANCE" : "vm1" })
with SmolVM() as vm2:
vm2.set_env_vars({ "INSTANCE" : "vm2" })
# vm2 cannot see vm1's variables
result = vm2.run( "echo $INSTANCE" )
print (result.output) # vm2
Atomic Updates
Variable updates are atomic - if the operation fails, the previous state is preserved:
try :
vm.set_env_vars({ "VALID_KEY" : "value" , "123invalid" : "bad" })
except ValueError :
print ( "Update failed - no changes were made" )
# File is unchanged
print (vm.list_env_vars()) # Previous state preserved
Error Handling
from smolvm import SmolVM
from smolvm.exceptions import SmolVMError
with SmolVM() as vm:
try :
# Invalid key
vm.set_env_vars({ "invalid-key" : "value" })
except ValueError as e:
print ( f "Validation error: { e } " )
try :
# VM must be running
vm.stop()
vm.set_env_vars({ "KEY" : "value" })
except SmolVMError as e:
print ( f "VM error: { e } " )
# Error: Cannot manage environment variables: VM is stopped
Next Steps
Command Execution Use environment variables in VM commands
Custom Images Build images with pre-baked environment configuration
AI Agent Integration Inject API keys for AI agent tools