Skip to content

Docker Compose Installation and Configuration Guide for Ubuntu

Overview

Docker Compose is an official tool for defining and running multi-container Docker applications. With Compose, you use a YAML configuration file to define your application's services, networks, and volumes. Then, using a single command, you create and start all the services from your configuration.

This comprehensive guide walks you through installing and configuring Docker Compose on Ubuntu, from prerequisites to advanced configuration options.


Prerequisites

Before installing Docker Compose, ensure you have:

  • Ubuntu System: Ubuntu 20.04, 22.04, 24.04, or newer
  • Docker Engine: Docker must be installed and running
  • Terminal Access: Root or sudo privileges
  • System Requirements: At least 2GB RAM and 20GB disk space
  • Internet Connection: For downloading packages and images

Important Note

Docker Compose V2 is the current version and uses the docker compose command (without hyphen). The older V1 version (docker-compose with hyphen) is now deprecated but still supported for backward compatibility.


Installation Methods

There are three primary methods to install Docker Compose on Ubuntu:

  1. Using Docker Repository (Recommended)
  2. Manual Binary Installation
  3. Docker Desktop (includes Docker Compose)

This is the most convenient and recommended method as it provides automatic updates through the system package manager.

Step 1: Update System Packages

First, update your package list to ensure you have access to the latest software versions:

sudo apt-get update

Step 2: Install Required Dependencies

Install the necessary packages for adding third-party repositories:

sudo apt install -y \
    ca-certificates \
    curl \
    gnupg \
    lsb-release \
    apt-transport-https \
    software-properties-common

Package explanations:

  • ca-certificates: Certificate authority for verifying third-party identities
  • curl: Command-line tool for data transfer
  • gnupg: GNU Privacy Guard for key management
  • lsb-release: Linux Standard Base version reporting utility
  • apt-transport-https: Enables HTTPS for apt repositories
  • software-properties-common: Scripts for managing software repositories

Step 3: Add Docker's Official GPG Key

Create a directory for Docker's GPG key and download it:

sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

Set appropriate permissions:

sudo chmod a+r /etc/apt/keyrings/docker.gpg

Step 4: Add Docker Repository

Add the Docker repository to your system's sources list:

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Step 5: Update Package Index

After adding the repository, update the package index:

sudo apt-get update

Step 6: Install Docker Compose Plugin

Install Docker Compose along with Docker Engine and related components:

sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

Components installed:

  • docker-ce: Docker Community Edition engine
  • docker-ce-cli: Docker command-line interface
  • containerd.io: Container runtime
  • docker-compose-plugin: Docker Compose V2 plugin

Step 7: Verify Docker Installation

Check if Docker is running:

sudo systemctl status docker

You should see output indicating that Docker is active and running.

Step 8: Verify Docker Compose Installation

Check the Docker Compose version:

docker compose version

Expected output:

Docker Compose version v2.40.0

Method 2: Manual Binary Installation

This method downloads the Docker Compose binary directly from GitHub. Use this if you need a specific version or cannot use the repository method.

Step 1: Determine Installation Location

For the current user only:

DOCKER_CONFIG=${DOCKER_CONFIG:-$HOME/.docker}
mkdir -p $DOCKER_CONFIG/cli-plugins

For all system users:

sudo mkdir -p /usr/local/lib/docker/cli-plugins

Step 2: Download Docker Compose Binary

For current user:

curl -SL https://github.com/docker/compose/releases/download/v2.40.0/docker-compose-linux-x86_64 -o $DOCKER_CONFIG/cli-plugins/docker-compose

For all users:

sudo curl -SL https://github.com/docker/compose/releases/download/v2.40.0/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose

Latest Version

Replace v2.40.0 with the latest version number from the Docker Compose releases page.

Step 3: Apply Executable Permissions

For current user:

chmod +x $DOCKER_CONFIG/cli-plugins/docker-compose

For all users:

sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose

Step 4: Verify Installation

docker compose version

Method 3: Legacy Installation (Standalone Binary)

Deprecated Method

This method installs the older docker-compose (with hyphen) command. Use only for backward compatibility with existing scripts.

sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

sudo chmod +x /usr/local/bin/docker-compose

# Create symbolic link
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

# Verify
docker-compose --version

Post-Installation Configuration

Enable Docker Service

Ensure Docker starts automatically on system boot:

sudo systemctl enable docker
sudo systemctl start docker

Manage Docker as Non-Root User

By default, Docker requires root privileges. To run Docker commands without sudo:

Step 1: Create Docker Group

The Docker group may already exist. Create it if needed:

sudo groupadd docker

Step 2: Add Your User to Docker Group

sudo usermod -aG docker $USER

Step 3: Apply Group Changes

Log out and log back in, or run:

newgrp docker

Step 4: Verify Non-Root Access

Test Docker without sudo:

docker run hello-world

Security Consideration

Adding users to the docker group grants root-level privileges. Only add trusted users to this group.

Test Docker Installation

Run a simple test to verify Docker is working:

docker run hello-world

If successful, you'll see a message confirming Docker is properly installed.


Understanding Docker Compose Files

Docker Compose uses YAML files to define multi-container applications. The default filename is compose.yaml (or compose.yml), though docker-compose.yaml and docker-compose.yml are also supported for backward compatibility.

Basic File Structure

version: '3.8'

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
    networks:
      - app-network
    volumes:
      - ./html:/usr/share/nginx/html

  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_PASSWORD: example
      POSTGRES_DB: myapp
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  db-data:

Key Components Explained

Version

Specifies the Compose file format version:

version: '3.8'

Note

The version field is optional in newer versions of Docker Compose but recommended for compatibility.

Services

Each service represents a container. Services can be based on an image or built from a Dockerfile:

Using an existing image:

services:
  redis:
    image: redis:alpine

Building from a Dockerfile:

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile

Ports

Map container ports to host ports:

ports:
  - "8080:80"        # host:container
  - "127.0.0.1:8000:8000"  # bind to specific interface

Volumes

Define persistent storage:

Named volumes:

services:
  db:
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

Bind mounts:

services:
  web:
    volumes:
      - ./app:/app              # relative path
      - /host/path:/container/path  # absolute path
      - data-volume:/data:ro    # read-only mount

Networks

Define custom networks for service communication:

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge

Assign services to networks:

services:
  web:
    networks:
      - frontend
      - backend

Environment Variables

Set environment variables in multiple ways:

Direct definition:

services:
  web:
    environment:
      DEBUG: "true"
      DATABASE_HOST: db
      DATABASE_PORT: 5432

From a file:

services:
  web:
    env_file:
      - .env
      - .env.production

Dependencies

Control service startup order:

services:
  web:
    depends_on:
      - db
      - redis

Docker Compose File Examples

Example 1: Simple Web Application

version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html:ro
    restart: unless-stopped

Example 2: WordPress with MySQL

version: '3.8'

services:
  wordpress:
    image: wordpress:latest
    ports:
      - "8000:80"
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress_password
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress-data:/var/www/html
    depends_on:
      - db
    networks:
      - wordpress-network

  db:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress_password
      MYSQL_ROOT_PASSWORD: root_password
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - wordpress-network
    restart: unless-stopped

networks:
  wordpress-network:
    driver: bridge

volumes:
  wordpress-data:
  db-data:

Example 3: Multi-Container Application Stack

version: '3.8'

services:
  frontend:
    build: ./frontend
    ports:
      - "3000:3000"
    networks:
      - app-network
    depends_on:
      - backend
    environment:
      - API_URL=http://backend:5000

  backend:
    build: ./backend
    ports:
      - "5000:5000"
    networks:
      - app-network
    depends_on:
      - database
      - redis
    environment:
      - DATABASE_URL=postgresql://user:password@database:5432/appdb
      - REDIS_URL=redis://redis:6379

  database:
    image: postgres:15-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: appdb
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - app-network

  redis:
    image: redis:alpine
    networks:
      - app-network
    volumes:
      - redis-data:/data

networks:
  app-network:
    driver: bridge

volumes:
  postgres-data:
  redis-data:

Example 4: Development Environment with Hot Reload

version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - ./app:/app
      - /app/node_modules
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=development
      - CHOKIDAR_USEPOLLING=true
    command: npm run dev

Essential Docker Compose Commands

Starting Services

Start all services defined in compose.yaml:

docker compose up

Start in detached mode (background):

docker compose up -d

Build images before starting:

docker compose up --build

Recreate containers:

docker compose up --force-recreate

Stopping Services

Stop all running services:

docker compose stop

Stop and remove containers:

docker compose down

Stop and remove containers, networks, volumes:

docker compose down --volumes

Remove all stopped containers:

docker compose down --remove-orphans

Managing Services

List running services:

docker compose ps

View logs:

docker compose logs

Follow logs in real-time:

docker compose logs -f

View logs for specific service:

docker compose logs web

Execute command in running container:

docker compose exec web bash

Run one-off command:

docker compose run web python manage.py migrate

Building and Pulling

Build or rebuild services:

docker compose build

Build specific service:

docker compose build web

Pull latest images:

docker compose pull

Advanced Commands

Validate compose file:

docker compose config

Pause services:

docker compose pause

Unpause services:

docker compose unpause

Restart services:

docker compose restart

Scale services:

docker compose up --scale web=3

View resource usage:

docker compose top

Environment Variables Configuration

Using .env File

Create a .env file in the same directory as your compose.yaml:

# .env file
POSTGRES_VERSION=15
DATABASE_PASSWORD=secure_password
APP_PORT=8000
NODE_ENV=production

Reference variables in compose.yaml:

version: '3.8'

services:
  db:
    image: postgres:${POSTGRES_VERSION}-alpine
    environment:
      POSTGRES_PASSWORD: ${DATABASE_PASSWORD}

  web:
    build: .
    ports:
      - "${APP_PORT}:5000"
    environment:
      NODE_ENV: ${NODE_ENV}

Variable Substitution Syntax

Basic substitution:

image: postgres:${POSTGRES_VERSION}

Default value if not set:

image: postgres:${POSTGRES_VERSION:-14}

Required variable (fails if not set):

image: postgres:${POSTGRES_VERSION?Variable not set}

Using Multiple Environment Files

Specify a custom env file:

docker compose --env-file .env.production up

Environment Variable Precedence

From highest to lowest priority:

  1. Shell environment variables
  2. Variables from --env-file flag
  3. Variables from default .env file
  4. Default values in compose.yaml

Best Practices for Environment Variables

Security Warning

Never commit .env files containing sensitive data to version control. Use .env.example with dummy values instead.

Create .env.example:

# .env.example
POSTGRES_VERSION=15
DATABASE_PASSWORD=change_me
APP_PORT=8000
NODE_ENV=development

Add to .gitignore:

.env
.env.local
.env.production

Working with Multiple Compose Files

You can use multiple compose files to organize configurations for different environments.

Base Configuration

docker-compose.yml:

version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"

Development Override

docker-compose.override.yml (automatically loaded):

version: '3.8'

services:
  web:
    volumes:
      - ./app:/app
    environment:
      DEBUG: "true"
    command: python manage.py runserver 0.0.0.0:8000

Production Configuration

docker-compose.prod.yml:

version: '3.8'

services:
  web:
    environment:
      DEBUG: "false"
    restart: always
    deploy:
      replicas: 3

Using Specific Files

# Development (default)
docker compose up

# Production
docker compose -f docker-compose.yml -f docker-compose.prod.yml up

# Testing
docker compose -f docker-compose.yml -f docker-compose.test.yml up

Advanced Configuration Topics

Resource Limits

Limit CPU and memory usage:

services:
  web:
    image: nginx
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

Health Checks

Define health check for services:

services:
  web:
    image: nginx
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

Restart Policies

Control container restart behavior:

services:
  web:
    image: nginx
    restart: unless-stopped

Available options:

  • no: Never restart
  • always: Always restart
  • on-failure: Restart only on failure
  • unless-stopped: Restart unless manually stopped

Logging Configuration

Configure logging drivers:

services:
  web:
    image: nginx
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Security Options

Run containers with enhanced security:

services:
  web:
    image: nginx
    security_opt:
      - no-new-privileges:true
    read_only: true
    tmpfs:
      - /tmp
    user: "1000:1000"

Using Secrets (Docker Swarm)

Store sensitive data securely:

version: '3.8'

services:
  db:
    image: postgres
    secrets:
      - db_password

secrets:
  db_password:
    file: ./db_password.txt

Troubleshooting Common Issues

Issue 1: Command Not Found

Error:

bash: docker-compose: command not found

Solutions:

  1. Verify installation:
docker compose version
  1. Check PATH configuration:
echo $PATH
which docker-compose
  1. Reinstall using repository method

Issue 2: Permission Denied

Error:

permission denied while trying to connect to the Docker daemon socket

Solutions:

  1. Add user to docker group:
sudo usermod -aG docker $USER
newgrp docker
  1. Use sudo (temporary):
sudo docker compose up

Issue 3: Port Already in Use

Error:

Bind for 0.0.0.0:80 failed: port is already allocated

Solutions:

  1. Find process using the port:
sudo lsof -i :80
sudo netstat -tulpn | grep :80
  1. Stop the conflicting process or change port in compose.yaml

Issue 4: Cannot Find Configuration File

Error:

Can't find a suitable configuration file in this directory

Solutions:

  1. Ensure you're in the correct directory:
ls -la compose.yaml docker-compose.yml
  1. Specify file explicitly:
docker compose -f path/to/compose.yaml up

Issue 5: Volume Mount Permission Issues

Error:

permission denied when accessing mounted volume

Solutions:

  1. Check file ownership:
ls -la /path/to/mounted/directory
  1. Adjust permissions:
sudo chown -R $USER:$USER /path/to/mounted/directory
  1. Run container with matching user ID:
services:
  web:
    user: "${UID}:${GID}"

Issue 6: Service Won't Start

Debugging steps:

  1. View logs:
docker compose logs service-name
  1. Check service status:
docker compose ps
  1. Inspect container:
docker compose exec service-name bash
  1. Validate compose file:
docker compose config

Issue 7: Network Connectivity Issues

Solutions:

  1. Inspect networks:
docker network ls
docker network inspect network-name
  1. Recreate networks:
docker compose down
docker compose up
  1. Verify DNS resolution:
docker compose exec web ping db

Security Best Practices

1. Run Containers as Non-Root User

services:
  web:
    image: nginx
    user: "1000:1000"

Or in Dockerfile:

FROM nginx
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser

2. Use Read-Only Filesystems

services:
  web:
    image: nginx
    read_only: true
    tmpfs:
      - /tmp
      - /var/run

3. Disable Privilege Escalation

services:
  web:
    image: nginx
    security_opt:
      - no-new-privileges:true

4. Limit Container Capabilities

services:
  web:
    image: nginx
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE

5. Use Secrets for Sensitive Data

Never hardcode passwords in compose files. Use secrets or environment files:

services:
  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    secrets:
      - db_password

secrets:
  db_password:
    file: ./secrets/db_password.txt

6. Keep Images Updated

Regularly update base images:

docker compose pull
docker compose up -d

7. Scan Images for Vulnerabilities

docker scan nginx:latest

8. Use Network Segmentation

Isolate services with different networks:

services:
  frontend:
    networks:
      - frontend-net

  backend:
    networks:
      - frontend-net
      - backend-net

  database:
    networks:
      - backend-net

networks:
  frontend-net:
  backend-net:

Performance Optimization

1. Use BuildKit

Enable BuildKit for faster builds:

export DOCKER_BUILDKIT=1
docker compose build

2. Optimize Image Layers

Use multi-stage builds in Dockerfiles:

FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY package*.json ./
RUN npm install --production
CMD ["node", "dist/index.js"]

3. Use Volume Caching

For development, cache dependencies:

services:
  app:
    volumes:
      - ./app:/app
      - /app/node_modules

4. Parallel Service Startup

Docker Compose starts services in parallel by default. Use dependencies wisely:

services:
  web:
    depends_on:
      db:
        condition: service_healthy

Monitoring and Logging

View Real-Time Logs

# All services
docker compose logs -f

# Specific service
docker compose logs -f web

# Last 100 lines
docker compose logs --tail=100

Monitor Resource Usage

# Container stats
docker stats

# Compose services
docker compose top

Export Logs

docker compose logs > application.log

Updating Docker Compose

Update via Repository

sudo apt-get update
sudo apt-get upgrade docker-compose-plugin

Update Manual Installation

Download the latest version:

COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4)
sudo curl -L "https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/lib/docker/cli-plugins/docker-compose
sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose

Complete Practical Example

Let's create a complete full-stack application with Docker Compose.

Project Structure

my-app/
├── docker-compose.yml
├── .env
├── .env.example
├── .dockerignore
├── frontend/
│   ├── Dockerfile
│   ├── package.json
│   └── src/
├── backend/
│   ├── Dockerfile
│   ├── requirements.txt
│   └── app/
└── nginx/
    ├── Dockerfile
    └── nginx.conf

docker-compose.yml

version: '3.8'

services:
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: my-app-frontend
    volumes:
      - ./frontend/src:/app/src
    networks:
      - app-network
    depends_on:
      - backend
    environment:
      - REACT_APP_API_URL=http://backend:5000

  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: my-app-backend
    volumes:
      - ./backend/app:/app
    networks:
      - app-network
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    environment:
      - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
      - REDIS_URL=redis://redis:6379
      - SECRET_KEY=${SECRET_KEY}

  db:
    image: postgres:15-alpine
    container_name: my-app-database
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - app-network
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:alpine
    container_name: my-app-redis
    volumes:
      - redis-data:/data
    networks:
      - app-network
    command: redis-server --appendonly yes

  nginx:
    build:
      context: ./nginx
      dockerfile: Dockerfile
    container_name: my-app-nginx
    ports:
      - "${NGINX_PORT:-80}:80"
      - "${NGINX_SSL_PORT:-443}:443"
    networks:
      - app-network
    depends_on:
      - frontend
      - backend
    restart: unless-stopped

networks:
  app-network:
    driver: bridge

volumes:
  postgres-data:
    driver: local
  redis-data:
    driver: local

.env.example

# Database Configuration
POSTGRES_USER=myapp_user
POSTGRES_PASSWORD=change_this_password
POSTGRES_DB=myapp_db

# Application Configuration
SECRET_KEY=your_secret_key_here
NODE_ENV=development

# Ports
NGINX_PORT=80
NGINX_SSL_PORT=443

Usage Commands

# Copy environment file
cp .env.example .env

# Edit environment variables
nano .env

# Start all services
docker compose up -d

# View logs
docker compose logs -f

# Stop services
docker compose down

# Rebuild and restart
docker compose up -d --build

# Clean up everything
docker compose down --volumes --remove-orphans

Quick Reference Cheat Sheet

Common Commands

Command Description
docker compose up Start services
docker compose up -d Start in background
docker compose down Stop and remove containers
docker compose ps List running services
docker compose logs View logs
docker compose logs -f Follow logs
docker compose exec <service> bash Execute shell in container
docker compose build Build services
docker compose pull Pull latest images
docker compose restart Restart services
docker compose stop Stop services
docker compose start Start stopped services
docker compose config Validate compose file

File Locations

Location Purpose
compose.yaml Primary compose file (preferred)
docker-compose.yml Legacy compose file name
.env Default environment variables
.dockerignore Exclude files from build context

Environment Variable Syntax

Syntax Description
${VAR} Substitute variable
${VAR:-default} Use default if not set
${VAR:?error} Show error if not set
${VAR-default} Use default only if VAR doesn't exist

Additional Resources

Official Documentation

Community Resources

Learning Materials


Conclusion

You now have a comprehensive understanding of installing and configuring Docker Compose on Ubuntu. This guide covered:

✅ Multiple installation methods (repository, manual, legacy) ✅ Post-installation configuration and user management ✅ Docker Compose file structure and syntax ✅ Essential commands and workflows ✅ Environment variable management ✅ Advanced configuration options ✅ Security best practices ✅ Troubleshooting common issues ✅ Performance optimization techniques ✅ Complete practical examples

With this knowledge, you can effectively manage multi-container applications using Docker Compose. Start with simple configurations and gradually incorporate advanced features as your requirements grow.

Remember to:

  • Keep Docker and Docker Compose updated
  • Follow security best practices
  • Use version control for compose files (except .env)
  • Document your configurations
  • Monitor resource usage
  • Regularly backup volumes and data

Happy containerizing!


Document Version: 1.0
Last Updated: October 2025
Compatibility: Ubuntu 20.04, 22.04, 24.04 and newer