Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time

Upgrading from v3 to v4

The configuration for v4 is somewhat compatible with the v3 configuration. It should be possible to reuse most of a v3 reconfiguration with minor tweaks.

This file describes the differences between v3 and v4. It does not contain a step-by-step process for upgrading the server.

In general, we have the following changes:

  • most module configuration is very close to v3.

  • most of the unlang processing is very close to v3.

  • each server section need a namespace parameter.

  • Packet processing sections are now recv Access-Request, etc. Not authorize, etc.

  • each listen section needs to be converted to the v4 format.

Upgrading from older versions

Upgrading from v3

When upgrading, please start with the default configuration of v4. Then, move your v3 configuration over, one module at a time. Check this file for differences in module configuration, and update the module to use the new configuration. Start the server after every change via radiusd -XC to see if the configuration is OK. Then, convert the listen sections, followed by the server sections.

Take your time. It is better to make small incremental progress, than to make massive changes, and then to spend weeks debugging it. Use a revision control system such as git to save and track your changes.

All of the attribute names used in v3 have been changed in v4. Please see the attribute names document for more information. This change was necessary in order to support the new "grouped" attributes, which are required for DHCPv6 and other protocols.

Upgrading from v2

If you are upgrading from v2 you should read the v3 version of this file. It describes changed from v2 to v3. This file describes only the changes from v3 to v4.


The following configurations have been removed. See the new listen sections for their replacements.

v3 v4


replaced with cleanup_delay in a listen section.


see mods-available/delay. You should list delay last in any send Access-Reject section.


see type = Status-Server in a new listen section.

The log section has been updated to remove many configuration items which are specific to RADIUS, and to Access-Request packets. Please see sites-available/default, and look for the Access-Request subsection there. See also templates.conf for a way to regain one global configuration for Access-Request packets.

Instantiate Section

The instantiate section has been removed. It originally started out as a way to ensure that modules were instantiated in a particular order. As of 3.0.9, listing modules in the instantiate section was no longer necessary. The functionality was left in version 3 for compatibility, but has now been removed from version 4.

The other use of the instantiate section was to define "virtual" modules for dynamic expansion. That functionality has been moved to the mods-available/ and mods-enabled/ directories. i.e. in version 4, just list the virtual module in a file, as if it was a real module.

See the redundant_sql module for more information.

Virtual Servers

There are some changes to the virtual servers in v4. First, every virtual server has to begin with an entry:

namespace = ...

For RADIUS, use:

namespace = radius

This tells the server what protocol is being used in that virtual server. This configuration was not necessary in v3, because each protocol was pretending to be RADIUS. That was simple to do and worked for some things, but it was not the best approach.

In v4, each protocol is completely independent, and RADIUS is no longer welded into the server core. i.e. The server core does modules, configuration files, policies, etc. RADIUS has been relegated to just another plug-in protocol, with the same status as DHCPv4 and DHCPv6.

Every example virtual server in the sites-enabled/ directory contains a namespace parameter. Please look at those files for examples of configuring and running each supported protocol.

Listen Sections

The listen sections have changed. There is now a type entry, which lists the packet type by their correct name (e.g._`Access-Request` instead of auth). To accept multiple kinds of packets, just list type multiple times:

type = Access-Request
type = Accounting-Request

Each listen section also has a transport entry. This configuration can be left out for headless servers, such as inner-tunnel. For example, setting UDP transport is done via:

transport = udp

Each type of transport has its configuration stored in a subsection named for that transport:

transport = udp
udp {
    ... udp transport configuration ...

For udp, the configuration entries are largely the same as for v3. e.g. ipaddr, port, etc.

The listen section then compiles each Processing Section based on the named packet types. It has a recv section for receiving packets, and a send section for sending packets, as seen in the following example:

recv Access-Request {
   ... unlang ...

send Access-Accept {
    ... unlang ...

This configuration is different from v3. The benefit of the change is that it is much easier to understand. Instead of using confusing names such as Post-Auth-Type Reject, the server now just uses send Access-Reject.

See also Processing Section for how the unlang statements are parsed.


The server supports global clients in the clients.conf file, as with v3.

Client can also be defined in a client subsection of a virtual server. Unlike v3, there is no need to have a clients section which "wraps" one or more client definitions. See sites-available/default for examples.

The server also supports dynamic clients. See sites-available/dynamic_clients for a worked example. There are many changes from v3. First, there is no need to have a client definition which contains a network. Instead, there is a network section which has a number of allow and deny rules. Second, dynamic clients can be defined on a per-connection basis. Finally, the sites-available/dynamic_clients virtual server has full access to the entire RADIUS packet.

The result of these changes is that it is now possible to have multiple clients behind a NAT gateway, and to have different shared secrets for each client. e.g._by keying off of the NAS-Identifier attribute.

The dynamic client functionality behaves the same for all protocols supported by the server. e.g. RADIUS, DHCP, VMPS, TACACS+, etc.

Processing Sections

All of the processing sections have been renamed. Sorry, but this was required for the new features in v4.

Old Name New Name


recv Access-Request


authenticate <Auth-Type>


send Access-Accept


recv Accounting-Request


accounting %{Acct-Status-Type}


send Accounting-Response


recv CoA-Request


send CoA-ACK


send CoA-NAK

Post-Auth-Type Reject

send Access-Reject

Post-Auth-Type Challenge

send Access-Challenge

i.e. instead of the section names being (mostly) randomly named, the names are now consistent. The recv sections receive packets from the network. The send sections send packets back to the network. The second name of the section is the type of the packet that is being received or sent.

For accounting, packets are also processed through an accounting section named after Acct-Status-Type. This process is similar to authenticate for Access-Request packets. The goal here is to allow a common pre-processing of accounting packets in the recv Accounting-Request packet, followed by type-specific processing in accounting %{Acct-Status-Type}. See sites-available/default for examples and more information.


Proxying has undergone massive changes. The proxy.conf file no longer exists, and everything in it has been removed. e.g. realm, home_server, home_server_pool no longer exist. The old proxying functionality was welded into the server core, which made many useful features impossible to configure.

The radius module now handles basic proxying to home servers. We recommend creating one instance of the radius module per home server. e.g.

radius home_server_1 {
   ... configuration for home server 1 ...

You can then use home_server_1 in any processing section, and the request will be proxied when processing reaches the module.

For ease of management, we recommend naming the modules for the host name of the home server.

It is often simplest to do proxying via an authenticate proxy { …​ } section, though that section can have any name. e.g. setting Auth-Type := proxy will call the authenticate proxy section, and is similar to the previous setting Proxy-To-Realm.

authenticate proxy {

For more detailed examples, see the Wiki page: That page also describes how to upgrade a v3 configuration to the new v4 style.

The benefit of this approach is that the "RADIUS proxy" functionality is just another module. It is now possible to not just fail over from one home server to another, but also to proxy the same packet to multiple destinations.


The home_server configuration has been replaced with the radius module. See raddb/mods-available/radius for examples and documentation.


The home_server_pool configuration has been replaced with standard unlang configurations. The various load-balancing options can be re-created using in-place unlang configuration.

The mappings for type are as follows:

  • type = fail-over - replaced with unlang

redundant {
Of course, you will have to use the names of the radius modules in your configuration, and not home_server_1, etc.
  • type = load-balance - replaced with unlang.

load-balance {
  • type = client-balance - replaced with unlang.

load-balance "%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}}" {
  • type = client-port-balance - replaced with unlang.

load-balance "%{%{Packet-Src-IP-Address}:-%{Packet-Src-IPv6-Address}}-%{Packet-Src-Port}" {
  • type = keyed-balance - replaced with unlang

load-balance "%{Load-Balance-Key}" {

While the Load-Balance-Key was a special attribute in v3, it has no special meaning in v4. You can use any attribute or string expansion as part of the load-balance key.

Things which were impossible in v3

In v3, it was impossible to proxy the same request to multiple destinations. This is now trivial. In any processing section, do:


When processing reaches that point, it will proxy the request to home_server_1, followed by home_server_2.

This functionality can be used to send Accounting-Request packets to multiple destinations.

You can also catch failed proxying, and do something else. In the example below, try to proxy to home_server_1, if that fails, just accept the request.

if (fail) {

CoA and Originate-Coa

The sites-available/originate-coa virtual server has been updated to use the new subrequest feature. Please see that virtual server, and the subrequest keyword for details.


The struct data type is now supported. See man dictionary.

Bit fields are now support via a data type such as bit[3]. Not that bit fields are only supported inside of a struct definition.

The dictionary parser includes many more sanity checks and helpful messages for people who create new dictionaries.

Dictionaries are now split up by protocol. e.g._`share/freeradius/radius/dictionary*`. All protocol-specific data types have been removed, and replaced with per-attribute flags.

The old abinary data type has been removed. Attributes needing this functionality should instead be marked with a flag, e.g._`string abinary`.

The old extended data type has been removed. Attributes needing this functionality should instead be marked with a flag, e.g._`tlv extended`.

"Tagged" RADIUS attributes

The old-style "tagged" RADIUS format has been removed. Instead of using

Tunnel-Type:1 = PPTP

you should use

Tag-1.Tunnel-Type = PPTP

It is also possible to "group" tagged attributes together, as in the following example:

Tag-1 = { Tunnel-Type = PPTP, Tunnel-Medium-Type = IPv4 }

There are 31 such attributes, Tag-1 through Tag-31. There is no Tag-0 attribute, as it is not needed.

After much investigation, it was unfortunately impossible to continue supporting the Attribute-Name:tag syntax for tagged attributes. This change requires modifications to all configuration files and databases which use tags. This change means also that detail files from v3 are not readable by v4.

Attribute references

In previous versions of the user attributes could be referred to by their name only e.g. if (User-Name == 'foo').

To allow for more thorough error checking, it is now required to prefix attribute references with &. Using bare names will result in an error, and a suggestion to use &.

Common places which will need to be checked and corrected are the left and right hand side of update {} sections, along with if conditions.

The v3 server has warned about using non prefixed attribute references for some time. If users have addressed those warnings, few modifications will be required.

Use of attributes in xlats e.g. %{User-Name} remains unchanged. There is no plan to require prefixes here.

As of v3, the preferred format for unknown attributes is &Attr-oid.oid.oid, e.g. &Attr-26.11344.255. However, v3 would still parse (but not generate) attributes of the form Vendor-FreeRADIUS-Attr-255. The Vendor- syntax has been removed in version 4. The server would never produce such names, and allowing them as input made attribute parsing significantly more complex.

List references

The old-style request: and reply: syntax for lists has been deprecated. Please use request. and reply. instead.

Many lists have been removed. e.g._`proxy`, proxy-reply, coa, coa-reply, disconnect, and disconnect-reply. The underlying functionality still exists, but it has been moved to different keywords, such as subrequest.

Update sections

The filtering operators <, >, <=, >=, etc. have been moved to the filter keyword. These operators will still be accepted in an update section, but using them will generate a warning.

Filter Sections

The filter section is now used to filter attributes.

The filtering operators have been modified. They no longer set the attribute to a value. Instead, they only filter the attribute list, and delete any attributes which do not match. The filtering operators do not create any attribute.

In order to achieve the same functionality as v3, you will need to set the value of the attribute via = in an update statement, and then filter it.

load-balance and redundant-load-balance sections

Before v4, the load-balance sections implemented load balancing by picking a child at random. This meant that load balancing was probabilistically fair, but not perfectly fair.

In v4, load-balance sections track how many requests are in each sub-section, and pick the subsection which is used the least. This is like the v3 proxy behavior of load balancing across home server pools.

The load-balance and redundant-load-balance sections now allow for a load-balance key:

load-balance "%{Calling-Station-Id}" {

If the key exists, it is hashed, and used to pick one of the subsections. This behavior allows for deterministic load-balancing of modules, similar to the v3 proxy keyed-balance configuration.

Connection timeouts

In v3 and earlier, the config items for configuring connection timeouts were either confusingly named, or completely absent in the case of many contributed modules.

In v4, connection timeouts can be configured universally for all modules with the connect_timeout config item of the module’s pool {} section.

The following modules will apply connect_timeout:

  • rlm_rest

  • rlm_linelog (network connections only)

  • rlm_ldap

  • rlm_couchbase

  • rlm_cache_memcached

  • rlm_redis_* (all the redis modules)

  • rlm_sql_cassandra

  • rlm_sql_db2

  • rlm_sql_freetds

  • rlm_sql_mysql

  • rlm_sql_unixodbc

Some modules such as rlm_sql_postgresql can have their timeout set via an alternative configuration item (e.g. radius_db in the case of postgresql).

Xlat expansions

The use of %% to escape % is no longer supported. Just use %, and the parser is smart enough to figure things out.

New Modules

The following modules are new in v4.


The radius module has taken over much of the functionality of proxy.conf. See raddb/mods-available/radius for documentation and configuration examples.

The radius module connects to one home server, just like the home_server configuration in v3. Some of the configuration items are similar to the home_server configuration, but not all.

The module can send multiple packet types to one home server. e.g. Access-Request and Accounting-Request.

This module also replaces the old coa and originate-coa configuration. See also subrequest for creating child requests that are different from the parent requests.

Unlike v3, the module can do asynchronous proxying. That is, proxying where the server controls the retransmission behavior. In v3, the server retransmitted proxied packets only when it received a retransmission from the NAS. That behavior is good, but there are times where retransmitting packets at the proxy is better.

Changed Modules

The following modules exhibit changed behaviour.


&control.Cache-Merge has been renamed to &control.Cache-Merge-New and controls whether new entries are merged into the current request. It defaults to no. The primary use case, is if you’re using xlat expansions in the cache module itself to retrieve information for caching, and need the result of those expensions to be available immediately.

Two new control attributes &control.Cache-Allow-Merge and &control.Cache-Allow-Insert have been added. These control whether existing entries are to be merged, and new entries created on the next call to a cache module instance. Both default to yes.


All certificate attributes are available in the &session-state. list, immediately after they are parsed from their ASN1 form.

The certificates are no longer added to the &request. list. Instead, they are added to the session-state list. You are advised to update any references during the upgrade to 4.0:


The rlm_eap_ikev2 module was removed. It does not follow RFC 5106, and no one was maintaining it.

The rlm_eap_tnc module was removed. No one was using or maintaining it.

The in-memory SSL cache was removed. Changes in OpenSSL and FreeRADIUS made it difficult to continue using the OpenSSL implementation of a cache. See raddb/sites-available/tls-cache for a better replacement. The OpenSSL cache can now be placed on disk, in memory, in memcache, or in a redis cache. The result is both higher performance, and more configurable.

The use_tunneled_reply and copy_request_to_tunnel configuration items have been removed. Their functionality has been replaced with the use_tunneled_reply and copy_request_to_tunnel policies. See raddb/sites-available/inner-tunnel and raddb/policy.d/eap for more information.

These configuration items were removed because they caused issues for a number of users, and they made the code substantially more complicated. Experience shows that having configurable policies in unlang is preferable to having them hard-coded in C.


The virtual_server configuration has been removed from EAP-PWD. The module now looks for &request.control.Password.Cleartext.


The LEAP protocol has been removed from the server. It is insecure, non-standard, and should not be used.


Exec-Program and Exec-Program-Wait have been removed.

The packet_type configuration has been removed. Use unlang checks to see if you want to execute the module.


Allow &Attr-Name[*] to mean sum. Previously, it just referred to the first attribute.

Using %{expr:0 + &Attr-Name[*]} will cause it to return the sum of the values of all attributes with the given name.

Note that %{expr:1 * &Attr-Name[*]} does not mean repeated multiplication. Instead, the sum of the attributes is taken as before, and then the result is multiplied by one.


The expiration module has been replaced with an unlang policy. The policy is located in raddb/policy.d/time. The Expiration attribute should continue to work the same as with v3.


The winbind_* configuration options are now in a winbind subsection. See mods-available/mschap for details.


Attributes of type octets are now passed directly to Perl as binary data, instead of as hex strings.

All data received from the network is marked tainted by default.


The case_sensitive option has been removed. Administrators should not be permitting users to log in with multiple different user names. If your system needs to be case insensitive, we suggest changing all names to lowercase:

recv Access-Request {
	update request {
		&Stripped-User-Name := "%{tolower:%{User-Name}}"


REST-HTTP-Code is now inserted into the &request. list instead of the &reply. list, to be compliant with the list usage guidelines.


Driver-specific options have moved from mods-available/sql to mods-config/sql/driver/<drivername>.


Now calls mysql_real_escape_string and no longer produces =<hexit><hexit> escape sequences in expanded values. The safe_characters config item is ignored when using MySQL databases.


Now calls PQescapeStringConn and no longer produces =<hexit><hexit> escape sequences in expanded values. The safe_characters config item is ignored when using PostgreSQL databases.


Attribute references:

The following config items must now be defined as attribute references
For example where in v3 you would specify the attribute names as
count_attribute = Acct-Session-Time
counter_name    = Daily-Session-Time
check_name      = Max-Daily-Session
reply_name      = Session-Timeout
key             = User-Name
In v4 they must now be specified as
count_attribute = &Acct-Session-Time
counter_name    = &Daily-Session-Time
check_name      = &control.Max-Daily-Session
reply_name      = &reply.Session-Timeout
key             = "%{%{Stripped-User-Name}:-%{User-Name}}"

Just adding the & prefix to the attribute name is not sufficient. Attributes must be qualified with the list to search in, or add to.

This allows significantly greater flexibility, and better integration with newer features in the server such as CoA, where reply_name can now be &coa:Session-Timeout. That allows the server to send a CoA packet which updates the Session-Timeout for the user.

In v4, when the key field was set to User-Name, the module would also look for Stripped-User-Name as the key. In v4, this functionality has been moved to the configuration. To get the same functionality, the key should now be specified as a dynamic expansion:

key = "%{%{Stripped-User-Name}:-%{User-Name}}"


The ipv6 configuration item has been deleted. It was deprecated in 3.0.16.

Instead, use attribute-name. See mods-available/sqlippool for more information.


The unix module uses Unix-Group instead of Group or Group-Name. Please use the new names instead of the old names, as the old names will no longer work.

Deleted Modules

The following modules have been deleted


Instead of using this, please use the sqlcounter module with sqlite.

It is difficult to maintain multiple implementations of the same functionality. As a result, we have simplified the server by removing duplicate functionality.


Instead of using this, please use the sql_ippool module with sqlite.

It is difficult to maintain multiple implementations of the same functionality. As a result, we have simplified the server by removing duplicate functionality.

Deleted Functionality

The Response-Packet-Type attribute has been removed. Please replace it with &reply.Packet-Type.